# TensorRT regionPlugin — NULL Pointer Dereference ![Severity: High](https://img.shields.io/badge/Severity-High%207.5%20CVSS-orange) ![CWE-476](https://img.shields.io/badge/CWE-476%20NULL%20Ptr%20Deref-red) ![CWE-789](https://img.shields.io/badge/CWE-789%20Unbounded%20Alloc-red) ![Status: Ready to submit](https://img.shields.io/badge/Status-Ready%20to%20Submit-blue) ## Target | Field | Value | |---|---| | **Repository** | https://github.com/NVIDIA/TensorRT | | **Platform** | huntr.com | | **Max payout** | $4,000 | | **Affected file** | `plugin/regionPlugin/regionPlugin.cpp` | | **Affected lines** | `allocateChunk` template (38–42), deserialization ctor (93–227), loop (152–168) | ## Vulnerability `allocateChunk()` calls `malloc(count * sizeof(T))` with **no NULL check** on the return value. The `count` parameter (`smTreeTemp->n`) is read directly from an attacker-controlled deserialization buffer with no bounds validation. Crafting a `.trt` engine file with `softmaxTree.n = INT32_MAX` causes: 1. `malloc(8,589,934,588)` → fails → returns `NULL` 2. Loop: `smTreeTemp->leaf[0]` → WRITE to address `0x0` → **SIGSEGV** ## Root Cause Snippet ```cpp // regionPlugin.cpp:38-42 — BUG template void allocateChunk(T*& ptr, int32_t count) { ptr = static_cast(malloc(count * sizeof(T))); // ← no NULL check here } // regionPlugin.cpp:117, 121 — attacker-controlled n flows into allocateChunk smTreeTemp->n = read(d); // ← 0x7FFFFFFF from crafted buffer allocateChunk(smTreeTemp->leaf, smTreeTemp->n); // malloc(8.5 GB) → NULL // regionPlugin.cpp:156 — NULL dereference smTreeTemp->leaf[i] = read(d); // WRITE to 0x0 → SIGSEGV ``` ## Trigger Path ``` crafted .trt file → Runtime.deserialize_cuda_engine(data) → RegionPluginCreator::deserializePlugin(name, serialData, serialLength) → new Region(serialData, serialLength) [regionPlugin.cpp:93] → allocateChunk(smTreeTemp->leaf, INT32_MAX) [regionPlugin.cpp:121] → malloc(8,589,934,588) returns NULL → smTreeTemp->leaf[0] = read(d) [regionPlugin.cpp:156] → SIGSEGV ``` ## Reproduction ```bash # Compile (no CUDA, no GPU, no TensorRT required) g++ -std=c++17 -O0 -o poc_tensorrt poc_tensorrt.cpp # Run ./poc_tensorrt ``` **Expected output:** ``` [*] n = 2147483647 (0x7FFFFFFF) [*] malloc requested: 8589934588 bytes (8.0 GB) [*] leaf ptr after allocateChunk: (nil) [!] malloc FAILED — leaf is NULL [!] Entering loop — crash on first iteration... Segmentation fault (core dumped) [exit 139 / SIGSEGV] ``` **ASAN confirmation:** ```bash g++ -std=c++17 -O0 -g -fsanitize=address -o poc_tensorrt_asan poc_tensorrt.cpp ASAN_OPTIONS=allocator_may_return_null=1 ./poc_tensorrt_asan # ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 # The signal is caused by a WRITE memory access. # Hint: address points to the zero page. # #0 in trigger_deserialize poc_tensorrt.cpp:187 ``` ## Suggested Fix ```cpp template void allocateChunk(T*& ptr, int32_t count) { ptr = static_cast(malloc(count * sizeof(T))); PLUGIN_VALIDATE(ptr != nullptr); // ADD THIS } ``` All 9 call sites in the deserialization constructor are fixed by patching the template. ## Files | File | Description | |---|---| | `poc_tensorrt.cpp` | Self-contained C++ PoC — compiles with g++, no CUDA | | `report.md` | Full huntr-format vulnerability report | | `poc-evidence.html` | HTML evidence page with terminal output and ASAN backtrace | | `README.md` | This file |