You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

hank-ai/darknet β€” int32 overflow in make_connected_layer() weight-buffer allocation -> heap-buffer-overflow READ in gemm_nt()

Status: gated, manual-approval PoC repository. For authorized security research / bug-bounty triage only (huntr.com MFF).

Target

  • Project: hank-ai/darknet (Darknet/YOLO, branch master)
  • Verified against commit: f684e1d75d4594298c8e73acc727ba6cf2e81c60 (2026-06-27, HEAD of master at verification time)
  • Vulnerable code: src-lib/connected_layer.cpp, make_connected_layer() / forward_connected_layer()
  • Class: CWE-190 Integer Overflow -> CWE-122/CWE-125 Heap-buffer-overflow (out-of-bounds READ)

This is a second, independent finding on the same target/CWE family as this campaign's darknet-cfg-int32-overflow claim (make_convolutional_layer()'s l.nweights computation β€” see that repo's notes: as originally crafted, that PoC's specific parameter choice does not reproduce a crash, because it also overflows the output-dimension computation into negative territory, which short-circuits the vulnerable gemm loop to zero iterations). This finding is in a completely different function (make_connected_layer, not make_convolutional_layer), and does reproduce a real crash end-to-end.

Root cause

// src-lib/connected_layer.cpp, make_connected_layer()
l.inputs = inputs;    // TRUE, unwrapped value stored
l.outputs = outputs;  // TRUE, unwrapped value stored
...
l.weights = (float*)xcalloc(outputs * inputs, sizeof(float));  // plain int*int, silently wraps

// forward_connected_layer(), called on every forward pass:
int k = l.inputs;    // the TRUE, unwrapped (large) value
int n = l.outputs;   // the TRUE, unwrapped (large) value
float *b = l.weights;  // the buffer allocated with the WRAPPED (small) size
gemm_cpu(0,1,m,n,k,1,a,k,b,k,1,c,n);
// -> dispatches to gemm_nt(1, n, k, ALPHA, A+t*lda, k, b, k, C+t*ldc, n) per batch row

outputs * inputs is a plain 32-bit int multiplication with no overflow check. inputs=65537, outputs=65536 gives 65537*65536 = 4,295,032,832 = 2^32 + 65536, which wraps to exactly 65536. l.weights is allocated with only 65536 real floats, but l.inputs (65537, the TRUE unwrapped value) is retained separately and used as both the gemm loop bound and row stride (ldb) in gemm_nt()'s real body:

// src-lib/gemm.cpp, gemm_nt()
for(k=0; k<K; ++k) sum += ALPHA*A[i*lda+k]*B[j*ldb + k];
// B == l.weights (65536-float buffer), ldb == l.inputs == 65537 (TRUE, large value)

Even row j=0 reads 65537 floats from a 65536-float allocation β€” an immediate heap buffer over-read, growing with every subsequent row.

Methodology

Building the full hank-ai/darknet CMake project was not feasible on this disk-constrained host: every real darknet-lib .cpp file unconditionally includes <opencv2/opencv.hpp> via darknet_internal.hpp, and CM_dependencies.cmake marks FIND_PACKAGE(OpenCV REQUIRED) β€” a full install pulls in several GB of transitive packages (ffmpeg, HDF5-MPI, ROCm/HIP, VTK, Java bindings, ...).

Instead, this harness compiles the exact real vulnerable function bodies β€” copy-pasted byte-for-byte unmodified from the fetched real sources (connected_layer_real.inc, taken verbatim from make_connected_layer()/forward_connected_layer() in the real connected_layer.cpp; real gemm_nt(), xcalloc_location(), fill_cpu(), axpy_cpu() are also compiled verbatim into repro_connected.cpp) against a minimal, explicitly-documented set of stand-ins for the surrounding (non-vulnerable) scaffolding (a trimmed Darknet::Layer/ Darknet::NetworkState struct with only the touched fields, a minimal ACTIVATION enum, and a gemm_cpu() dispatcher stand-in that reproduces the real dispatcher's behavior for the specific TA=0,TB=1 call forward_connected_layer() makes β€” verified against the real gemm.cpp that for TB=1 the AVX2/FMA fast path is unreachable, so the real code unconditionally calls the real gemm_nt() here too). None of the stand-ins touch the vulnerable arithmetic or the vulnerable memory access. See the top-of-file comment in repro_connected.cpp for full detail.

Proof of concept (asan_connected_output.txt)

[craft] inputs=65537 outputs=65536
[craft] true 64-bit product = 4295032832, wrapped 32-bit product = 65536

[call] Darknet::Layer l = make_connected_layer(1, 1, 65537, 65536, LINEAR, 0);  <-- REAL function
[alloc] l.weights allocated with 65536 floats (262144 bytes) -- l.inputs=65537, l.outputs=65536 (TRUE values retained)

[call] forward_connected_layer(l, state);  <-- REAL function, dispatches to REAL gemm_nt()

==1194476==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f7e873b3800
READ of size 4 at 0x7f7e873b3800 thread T0
    #0 ... in gemm_nt(int, int, int, float, float*, int, float*, int, float*, int) repro_connected.cpp:184
    #1 ... in gemm_cpu(...) repro_connected.cpp:206
    #2 ... in forward_connected_layer(Darknet::Layer&, Darknet::NetworkState) connected_layer_real.inc:126
    #3 ... in main repro_connected.cpp:307
0x7f7e873b3800 is located 0 bytes after 262144-byte region [0x7f7e87373800,0x7f7e873b3800)
allocated by thread T0 here:
    #0 ... in calloc
    #1 ... in xcalloc_location(...) repro_connected.cpp:151
    #2 ... in make_connected_layer(...) connected_layer_real.inc:35
SUMMARY: AddressSanitizer: heap-buffer-overflow repro_connected.cpp:184 in gemm_nt(...)

Reachability

make_connected_layer()/forward_connected_layer() are invoked for every [connected] section in a .cfg network definition and executed on the very first inference pass. inputs/outputs are attacker-controlled via the .cfg's output= fields (this layer's own output=, and the preceding layer's output=, which becomes this layer's inputs).

Files

  • repro_connected.cpp β€” harness (see file header for full methodology)
  • connected_layer_real.inc β€” real make_connected_layer()/forward_connected_layer() bodies, copied verbatim from src-lib/connected_layer.cpp
  • asan_connected_output.txt β€” captured AddressSanitizer heap-buffer-overflow report
  • poc.cpp β€” the original draft PoC (kept for provenance; it hand-reimplemented the vulnerable arithmetic rather than calling real darknet functions β€” this repo's repro_connected.cpp supersedes it with a real-function-based reproduction)

Build & run

g++ -std=c++17 -O0 -g -fsanitize=address,undefined -o repro_connected_asan repro_connected.cpp
./repro_connected_asan
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support