onnx-tensorrt convertAxis inclusive-bound OOB write (Split + Resize)
Root cause: convertAxis (importerUtils.cpp:306-317) validates an axis with an
INCLUSIVE upper bound axis >= 0 && axis <= nbDims. The <= nbDims was relaxed to
support QuantDequantLinearHelper (see the in-source comment). For an input of rank
R, an attacker-supplied axis == R therefore PASSES validation, and is then used to
index a std::vector sized exactly R, producing a one-element out-of-bounds WRITE
(std::vector::operator[] does no bounds check in a release build).
Pinned commit: 7c51a63a719180eb5160c874c111746f3fb46a6b
Instances (one consolidated finding)
| Instance | Site | Vector | Element written OOB |
|---|---|---|---|
| Split (headline, High) | onnxOpImporters.cpp:6350-6354 | std::vector<int64_t> tmp(inputDims.size()) |
int64 |
| Resize / scales | onnxOpImporters.cpp:5233-5235 + 5413-5417 | std::vector<float> tempVec(inputRank, 1.0) |
float |
| Resize / sizes | onnxOpImporters.cpp:5233-5235 + 5341-5344 | std::vector<int64_t> tempVec(inputDims...) |
int64 |
The static Resize checker (onnxOpCheckers.cpp:672-718) validates only mode and axis uniqueness, NOT axis vs rank, so there is no upstream pre-filter for the Resize path.
Files
harness.cpp— faithful standalone ASan harness. CopiesconvertAxisverbatim (with the<= nbDimsbound) and reproduces the exactvec[axis] = valuewrites from Split :6354, Resize/scales :5417, Resize/sizes :5344, driven byaxis == rank.make_onnx.py— builds the three crafted.onnxPoC models (input rank 3, offending axis/axes == 3).poc_split_axis_eq_rank.onnx,poc_resize_scales_axes_eq_rank.onnx,poc_resize_sizes_axes_eq_rank.onnx— the crafted inputs an attacker would feed to the parser.asan_split.txt,asan_resize_scales.txt,asan_resize_sizes.txt— captured ASan heap-buffer-overflow reports.
Build & run
clang++ -std=c++17 -g -O1 -fsanitize=address -fno-omit-frame-pointer \
-U_GLIBCXX_ASSERTIONS -U_FORTIFY_SOURCE -D_GLIBCXX_NO_ASSERTIONS \
harness.cpp -o harness
export ASAN_OPTIONS=detect_leaks=0
./harness split # heap-buffer-overflow WRITE size 8, 0 bytes after 24-byte region
./harness resize-scales # heap-buffer-overflow WRITE size 4, 0 bytes after 12-byte region
./harness resize-sizes # heap-buffer-overflow WRITE size 8, 0 bytes after 24-byte region
./harness split-control # axis < rank -> clean, no overflow
The libstdc++ hardening flags are turned OFF to match how upstream onnx-tensorrt is
compiled (release, no _GLIBCXX_ASSERTIONS), so operator[] performs a raw pointer
write and ASan observes the true heap overflow rather than a container assertion.
Harness faithfulness (stated honestly)
The vulnerable expressions are byte-for-byte the real lines: the convertAxis body
including the axis <= nbDims bound, the vector sizing (inputDims.size() /
inputRank), and the indexed writes. The only substitutions are the surrounding
TensorRT plumbing that does not participate in the bug: ShapeTensor/ITensor/
shapeOf are represented by a std::vector whose .size() is the tensor rank
(exactly what inputDims.size() returns in the importer), and ONNXTRT_CHECK_NODE is
reduced to its throw-on-false semantic. Building full onnx-tensorrt requires TensorRT;
the harness compiles the real vulnerable lines verbatim and drives them with the
attacker-controlled axis == rank value that the crafted .onnx carries.
Distinctness
Root cause here = convertAxis inclusive bound (importerUtils.cpp:314) flowing into
fixed-size vectors in Split/Resize. Shares no line with the 4 previously filed
onnx-tensorrt findings (external_data .. traversal; volume() int overflow;
parseExternalWeights offset OOB WeightsContext.cpp:212; GRU/RNN iterator OOB
onnxOpImporters.cpp:2825). No CVE exists for convertAxis axes OOB.