EnigmaConsultant's picture
Upload folder using huggingface_hub
eba7224 verified
|
Raw
History Blame Contribute Delete
3.91 kB

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. 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.