PoC β Heap out-of-bounds write & over-read in the Arm NN deserializer (.armnn model load)
Security research artifact for responsible disclosure (Protect AI / huntr Model File Vulnerability program). These files are malformed Arm NN model files that trigger memory-safety bugs in
ARM-software/armnn'sarmnnDeserializerwhen loaded. They are crash/where-it-corrupts proof-of-concepts, not weaponized exploits. Do not load them with an unhardened build outside a sandbox.
Affected project: ARM-software/armnn β armnnDeserializer
(src/armnnDeserializer/Deserializer.cpp β armnnUtils::Permute), reached via the public API
armnnDeserializer::IDeserializer::CreateNetworkFromBinary.
Version: HEAD f8beb5a9 (v26.01). Format: .armnn (Arm NN FlatBuffers).
Class: CWE-787 (OOB Write) / CWE-190 (Integer Overflow) / CWE-125 (OOB Read) / CWE-843 (Type Confusion).
Root cause
ParseConstant (default m_WeightsLayoutScheme <= 0 path) permutes constant-tensor weights into a freshly
allocated buffer. Two validation gaps make this memory-unsafe when the model is untrusted:
- Unchecked integer overflow.
TensorShape::GetNumElements()multiplies the dimensions inunsigned intwith no overflow check (src/armnn/Tensor.cpp:181), andGetNumBytes() = GetDataTypeSize() * GetNumElements()likewise. A dimension product of2^32wraps to0, sopermuteBuffer = new unsigned char[GetNumBytes()]is under-allocated, whilearmnnUtils::Permuteiterates the true product of dimensions β OOB write. - No data-type/width cross-check.
ToConstTensoronly checksdata->size() == GetNumElements()(element count); it never verifies the union member width (ByteData/ShortData/IntData/LongData= 1/2/4/8 bytes) matches the tensor's declareddataTypewidth. This (a) lets the over-read readGetNumBytes()from a smaller buffer β OOB read whose content surfaces in the returnedINetwork(heap disclosure), and (b) lets the source stay larger than the destination so the OOB write content is attacker-supplied.
armnnUtils::Permute/PermuteLoop::Unroll receive srcEnd/dstEnd but only null-check them β they are never
enforced as bounds at the memcpy.
Files (models/)
| file | effect when loaded (under ASAN) |
|---|---|
combined_chain.armnn |
one model, both bugs β two ConstantLayers (write + leak), single CreateNetworkFromBinary call. ASAN aborts at the first OOB (write-first β WRITE). |
constweight_controlled_write.armnn |
controlled-content heap OOB WRITE β dims=[1,1,2,2147483680] (product 2^32+64 β wraps to 64), dataType=QAsymmU8 (dst=64 B), data via IntData (256 B of 0x41). Overflowing byte written at dstOff=64 is the attacker's 0x41. |
constweight_widthconfusion.armnn |
heap OOB READ / info-leak β Float32 tensor (GetNumBytes()=4000) with ByteData of only 1000 B β 3000 B over-read copied into the network constant. |
constweight_allocoverflow.armnn |
minimal length-only WRITE variant β dims=[65536,65536,1,1], empty data β new[0] β WRITE of size 4 at offset 0. |
*.json are the human-readable sources (compiled to .armnn with flatc -b ArmnnSchema.fbs <f>.json).
asan_*.txt are the full AddressSanitizer reports.
Reproduce
# 1. flatbuffers v24.3.25 (flatc + libflatbuffers.a)
# 2. Build armnn deserializer only, no ACL backends, with ASAN:
cmake -GNinja .. -DBUILD_ARMNN_DESERIALIZER=ON -DARMNNREF=OFF -DBUILD_UNIT_TESTS=OFF \
-DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_CXX_FLAGS="-fsanitize=address -g -O1" \
-DFLATC_DIR=<flatc_dir> -DFLATBUFFERS_INCLUDE_PATH=<inc> -DFLATBUFFERS_LIBRARY=<libflatbuffers.a>
ninja armnnDeserializer
# (Deserializer.cpp + armnnUtils compiled with -DNDEBUG to match a Release build, so the
# flatbuffers debug assert is removed and the real OOB access is exercised.)
# 3. A ~15-line driver that calls IDeserializer::CreateNetworkFromBinary(bytes):
./driver models/constweight_controlled_write.armnn # -> AddressSanitizer: heap-buffer-overflow WRITE
./driver models/constweight_widthconfusion.armnn # -> AddressSanitizer: heap-buffer-overflow READ
./driver models/combined_chain.armnn # -> WRITE (first layer)
Disclosure
Reported responsibly via the huntr / Protect AI Model File Vulnerability program; Arm notified in parallel. No weaponized control-flow-hijack chain is included β these are the underlying memory-safety primitives only.