Views
No views yet
BVLC/caffe — the reference Caffe implementation (~34k stars, archived)
Class: CWE-190 Integer Overflow / CWE-122 Heap-based Buffer Overflow / CWE-121 Stack-based Buffer Overflow — attacker-controlled protobuf fields from a .caffemodel cause integer overflow in layer dimension arithmetic, leading to buffer overflow WRITEs.
Status: TOOL-VERIFIED under AddressSanitizer (2 distinct crash variants). 2026-06-12.(.prototxt, .caffemodel) pair. The .caffemodel is a binary protobuf (NetParameter) containing all layer parameters and weights. The canonical load path is:1Net<float> net("model.prototxt", caffe::TEST);
2net.CopyTrainedLayersFrom("model.caffemodel");
3// Or: caffe test -model model.prototxt -weights model.caffemodel.caffemodel + .prototxt with crafted layer parameters causes memory corruption during layer setup and the first forward pass. The attacker fully controls all protobuf fields (layer types, parameter values, blob dimensions). Caffe performs zero integer overflow validation on arithmetic derived from these fields.| # | Bug | Primitive | Function / line | Trigger field | Verified |
|---|---|---|---|---|---|
| 1 | TileLayer tiles * shape(axis) overflow | heap-buffer-overflow WRITE | TileLayer::Forward_cpu → caffe_copy tile_layer.cpp:30 | TileParameter.tiles | ✅ ASAN |
| 2 | im2col 2*pad_h overflow | stack-buffer-overflow WRITE | im2col_cpu im2col.cpp:37 | ConvolutionParameter.pad_h | ✅ ASAN |
int multiplication of attacker-controlled protobuf fields, where the overflow produces a value that is either (a) much smaller than the true product (causing undersized allocation → overflow on write) or (b) directly causes an OOB write in a loop.tiles) and the write content (via input data/weights).tile_layer.cpp:17 computes the output dimension with an unchecked int multiplication:1// tile_layer.cpp:17 — TileLayer::Reshape
2top_shape[axis_] = bottom[0]->shape(axis_) * tiles_;tiles_ is read directly from the protobuf TileParameter.tiles (int32) at line 14. With shape(axis_) = 4 and tiles_ = 1073741825 (0x40000001):top_shape[axis_] becomes 4 instead of 4.29 billion. top[0]->Reshape(top_shape) allocates for count = 4 (16 bytes). Blob::Reshape's overflow guard (CHECK_LE(shape[i], INT_MAX / count_)) sees the value 4 and passes — the damage is already done before the guard.Forward_cpu (lines 28–34) iterates outer_dim_ * tiles_ times, each calling caffe_copy(inner_dim_, ...):1for (int i = 0; i < outer_dim_; ++i) {
2 for (int t = 0; t < tiles_; ++t) {
3 caffe_copy(inner_dim_, bottom_data, top_data);
4 top_data += inner_dim_; // advances past the 16-byte buffer
5 }
6 bottom_data += inner_dim_;
7}caffe_copy call.==ERROR: AddressSanitizer: heap-buffer-overflow WRITE of size 16 at 0x...26a0 thread T0
#0 memcpy sanitizer_common_interceptors_memintrinsics.inc:117
#1 caffe::caffe_copy<float> src/caffe/util/math_functions.cpp:96
#2 caffe::TileLayer<float>::Forward_cpu src/caffe/layers/tile_layer.cpp:30
#3 caffe::Layer<float>::Forward include/caffe/layer.hpp:419
0x...26a0 is located 0 bytes after 16-byte region [0x...2690,0x...26a0)
allocated by ... caffe::CaffeMallocHost include/caffe/syncedmem.hpp:30findings/caffe_evidence/tile_heap_overflow.txt)(tiles - 1) * inner_dim * sizeof(float) bytes past the 16-byte heap chunk. Adjacent heap objects include other blob data/metadata, layer structs with function pointers. This is a straightforward heap corruption → RCE candidate..prototxt layer graph, so they choose the allocation sequence → deterministic adjacency.TileLayer::Forward_cpu, which is called on the first inference after model loading. Many Caffe uses (classification, detection) call forward immediately after load.2*pad_h integer overflow → stack-buffer-overflow WRITEim2col.cpp:25–28 computes output dimensions with unchecked int arithmetic:1const int output_h = (height + 2 * pad_h -
2 (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;pad_h is read from ConvolutionParameter.pad_h (uint32 in proto, stored as int). With pad_h = 1073741823 (0x3FFFFFFF): 2 * pad_h = 2147483646, and height + 2147483646 overflows int32, producing a negative or unexpected output_h. The inner write loop at line 37 (*(data_col++) = 0) then writes past the column buffer.==ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 4 at 0x... thread T0
#0 caffe::im2col_cpu<float> src/caffe/util/im2col.cpp:37
0x... is located in stack of thread T0 ... overflows 'data_col' variablefindings/caffe_evidence/im2col_stack_overflow.txt)BaseConvolutionLayer::forward_cpu_gemm → conv_im2col_cpu during inference on any model with a convolutional layer. The pad values come directly from the .caffemodel/.prototxt ConvolutionParameter.int multiplications without bounds checksDCHECK (debug-only assertions) used for index bounds in EmbedLayer — no-ops in release buildsBlobShape.dim is int64 in the proto but silently truncated to int in Blob::ReshapeBlob::Reshape CHECK_LE) is bypassed by the TileLayer overflow because the truncated product passes the per-step check1# In the Caffe source tree, after building with ASAN:
2# g++ -std=c++14 -fsanitize=address -g -O0 -DCPU_ONLY -Iinclude -I.build_release/src ...
3# Link: blob.o syncedmem.o common.o caffe.pb.o math_functions.o tile_layer.o
4
5# Harness creates TileLayer with tiles=1073741825, bottom=[1,4,1,1] (axis=1)
6# 4 * 1073741825 overflows to 4, alloc=16 bytes, Forward writes ~16GB past buffer
7./harness_tile1# Harness calls im2col_cpu with pad_h=1073741823
2# 2*pad_h = 2147483646, height+2*pad_h overflows int → OOB write
3./harnesspoc/mfv_caffe_tile.cpp (BUG 1), poc/mfv_caffe_im2col.cpp (BUG 2).
Build flags: g++ -std=c++14 -fsanitize=address -g -O0 -DCPU_ONLY -I<caffe>/include -I<caffe>/.build_release/srcTileLayer::Reshape:1CHECK_LE(tiles_, INT_MAX / bottom[0]->shape(axis_))
2 << "tiles * shape overflows int";
3top_shape[axis_] = bottom[0]->shape(axis_) * tiles_;int64_t intermediates):1int64_t output_h_64 = ((int64_t)height + 2 * (int64_t)pad_h -
2 ((int64_t)dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
3CHECK_LE(output_h_64, INT_MAX) << "output dimension overflow";