# 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 tmp(inputDims.size())` | int64 | | Resize / scales | onnxOpImporters.cpp:5233-5235 + 5413-5417 | `std::vector tempVec(inputRank, 1.0)` | float | | Resize / sizes | onnxOpImporters.cpp:5233-5235 + 5341-5344 | `std::vector 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. Copies `convertAxis` verbatim (with the `<= nbDims` bound) and reproduces the exact `vec[axis] = value` writes from Split :6354, Resize/scales :5417, Resize/sizes :5344, driven by `axis == rank`. - `make_onnx.py` — builds the three crafted `.onnx` PoC 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.