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.