Arm NN ONNX Unsqueeze out-of-range-axis heap-buffer-overflow write
This repository contains a minimal differential reproduction for a native
heap-buffer-overflow write in Arm NN's ONNX Unsqueeze parser.
Arm NN commit:
2b61cecc9df7a43fca1463795062cf359e6be820
Files
models/control-axis-zero.onnx: checker-validUnsqueezemodel withaxes=[0]; parses successfully.models/trigger-axis-eight.onnx: checker-validUnsqueezemodel withaxes=[8]; AddressSanitizer reports a four-byte heap-buffer-overflow write during model loading.generate_models.py: deterministic ONNX fixture generator.harness.cpp: minimal public-API loader.asan-output.txt: representative sanitizer report.armnn-onnx-unsqueeze-axis-oob-write-poc.zip: source, fixtures, and logs.
Root cause
src/armnnOnnxParser/OnnxParser.cpp:2428-2431 inserts a dimension at each
model-controlled axis without first checking that the axis is no greater than
the vector's current size:
for(uint i = 0; i < dims.size(); i++)
{
targetShape.insert(targetShape.begin() + armnn::numeric_cast<int>(dims[i]), 1);
}
For the rank-one input in the trigger, targetShape.size() is one. The model's
axis value of eight forms an invalid iterator beyond the vector allocation.
The subsequent std::vector::insert writes four bytes into poisoned heap
memory.
Reproduction
Build Arm NN with its ONNX parser and ASan/UBSan, compile harness.cpp, then:
./harness models/control-axis-zero.onnx
ASAN_OPTIONS=abort_on_error=1:detect_leaks=0 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
./harness models/trigger-axis-eight.onnx
Expected control:
parsed successfully
Expected trigger:
ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 4
#2 armnnOnnxParser::OnnxParserImpl::ParseUnsqueeze(...)
0x... is located 24 bytes after 8-byte region [0x...,0x...)
SUMMARY: AddressSanitizer: heap-buffer-overflow
Three interleaved control runs exited 0. Three trigger runs exited 134 with the same sanitizer signature.
Fixture integrity
9a69aeeda881718fd5e10e0d3bdac1498920149fed04f707faae406475180437 control-axis-zero.onnx
4bad644776b7e848a571aa29a2425f67771eb8c405ed3ef0d35f10305d0e40c6 trigger-axis-eight.onnx
Both files are 166 bytes and pass ONNX 1.18.0's
onnx.checker.check_model(). Their security-relevant difference is the
axes value: zero versus eight.