You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Heap out-of-bounds READ in TensorRT Region_TRT plugin deserialize constructor via unchecked serialized softmaxTree counts (n / groups)

Summary

The Region_TRT plugin shipped in TensorRT-OSS deserializes its inline plugin blob from an attacker-supplied engine (.plan / .engine) file without validating attacker-controlled element counts against the number of bytes physically remaining in the blob. A forged softmaxTree.n (or softmaxTree.groups) count drives an unbounded loop of unchecked 4-byte read<int32_t>() calls that walk off the end of the heap buffer holding the plugin blob, producing a heap-buffer-overflow READ. The only length check in the constructor runs at the very end, after every read loop has already executed.

Target

  • Repository: https://github.com/NVIDIA/TensorRT (TensorRT-OSS plugin library, Apache-2.0; compiled into libnvinfer_plugin.so shipped with every TensorRT release)
  • Version: VERSION file = 11.1.0.106, HEAD of main
  • Vulnerable file/function: plugin/regionPlugin/regionPlugin.cpp β€” Region::Region(void const* buffer, size_t length), lines 94–228
  • Primitive helper: plugin/common/plugin.h β€” read<>(), lines 100–109
  • Struct: include/NvInferPluginUtils.h β€” struct softmaxTree, lines 103–114

Trigger path

IRuntime::deserializeCudaEngine() parses an attacker-supplied engine that contains an IPluginV2Layer of type Region_TRT (registered via REGISTER_TENSORRT_PLUGIN). The engine reader hands the plugin's inline serialized-data blob to the creator:

IRuntime::deserializeCudaEngine(plan)
  -> RegionPluginCreator::deserializePlugin(name, serialData, serialLength)
       -> std::make_unique<Region>(serialData, serialLength)
            -> Region::Region(void const* buffer, size_t length)   // vulnerable

serialData / serialLength come verbatim from the attacker-controlled plan file.

Root cause

The deserialize constructor reads a 32-bit count smTreeTemp->n directly out of the blob, allocates the leaf/parent/child/group chunks with allocateChunk (malloc(n * sizeof(T))), then immediately loops n times pulling 4-byte values with read<int32_t>(d):

smTreeTemp->n = read<int32_t>(d);          // attacker-controlled count, unchecked

if (leafPresent) { allocateChunk(smTreeTemp->leaf, smTreeTemp->n); } ...

for (int32_t i = 0; i < smTreeTemp->n; i++)      // unbounded read loop
{
    if (leafPresent)   { smTreeTemp->leaf[i]   = read<int32_t>(d); }   // OOB read
    if (parentPresent) { smTreeTemp->parent[i] = read<int32_t>(d); }
    if (childPresent)  { smTreeTemp->child[i]  = read<int32_t>(d); }
    if (groupPresent)  { smTreeTemp->group[i]  = read<int32_t>(d); }
}

read<>() is an unconditional memcpy of sizeof(OutType) that advances the cursor with no bound check:

template <typename OutType, typename BufferType>
OutType read(BufferType const*& buffer)
{
    static_assert(sizeof(BufferType) == 1, "BufferType must be a 1 byte type.");
    OutType val{};
    std::memcpy(&val, static_cast<void const*>(buffer), sizeof(OutType));
    buffer += sizeof(OutType);
    return val;
}

The same pattern repeats for the name loop (256 read<char> per node) and the groups loop (smTreeTemp->groups, another unchecked serialized count). n / groups are never validated against the bytes remaining. The only length check, PLUGIN_VALIDATE(d == a + length), runs at the very end of the constructor β€” after every read loop has already completed and the OOB reads have already happened.

By setting softmaxTreePresent=1, leafPresent=1 and inflating n while supplying a short payload, the leaf read loop walks read<int32_t>(d) past the end of the heap allocation.

Proof of concept

A faithful standalone ASan harness reuses the real, unmodified TensorRT-OSS code for read<>(), struct softmaxTree, allocateChunk(), and the verbatim body of the Region deserialize constructor. Only the destruction-time SoftmaxTreeDeleter binding is simplified (a default deleter) β€” it runs strictly after the vulnerable loops and is irrelevant to the OOB read.

The malicious 48-byte Region_TRT blob is heap-allocated (mirroring how the closed-source engine reader hands a heap buffer to the plugin creator): 6 int32 header fields (C,H,W,num,classes,coords), 8 presence bools with softmaxTreePresent=1 and leafPresent=1 (rest 0), softmaxTree.n forged to 100000, then only 3 real leaf int32 values. The leaf read loop consumes the 3 valid values, then read<int32_t>(d) on iteration i=3 reads 4 bytes at offset 48 β€” 0 bytes past the 48-byte region β€” a heap-buffer-overflow READ.

Build:

clang++ -std=c++17 -O0 -g -fsanitize=address -fno-omit-frame-pointer region_harness.cpp -o region_asan

Files: region_harness.cpp (attack), region_negctl.cpp (negative control).

Captured evidence (verbatim)

[*] blob heap alloc = 48 bytes; softmaxTree.n (attacker) = 100000
[*] leaf loop will read 100000 int32 (400000 bytes) from a 48-byte blob
[*] calling Region::Region(serialData, serialLength) ...
==1895553==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b88847e0080 at pc 0x55bbdfa1561f bp 0x7fffdd995100 sp 0x7fffdd9948c0
READ of size 4 at 0x7b88847e0080 thread T0
    #0 0x55bbdfa1561e in __asan_memcpy
    #1 0x55bbdfa5ef45 in int nvinfer1::plugin::read<int, char>(char const*&) region_harness.cpp:76:5
    #2 0x55bbdfa5dd43 in deserializeRegion(void const*, unsigned long) region_harness.cpp:164:58
    #3 0x55bbdfa5d382 in main region_harness.cpp:245:9

0x7b88847e0080 is located 0 bytes after 48-byte region [0x7b88847e0050,0x7b88847e0080)
allocated by thread T0 here:
    #0 0x55bbdfa179c8 in malloc
    #1 0x55bbdfa5d192 in main region_harness.cpp:219:43

SUMMARY: AddressSanitizer: heap-buffer-overflow in __asan_memcpy

The heap-buffer-overflow READ occurs inside read<int32_t>() (frame #1), called from the leaf read loop in the verbatim Region deserialize body (frame #2, region_harness.cpp:164), exactly at the point of the unchecked read<int32_t>(d).

Negative control

A well-formed 52-byte blob with n=3 exactly matching 3 leaves plus trailing groups=0 deserializes cleanly, confirming the crash is specific to the count/length mismatch and not a harness artifact:

--- negative control ---
[*] well-formed blob = 52 bytes, softmaxTree.n = 3 (matches 3 leaves)
[+] deserialize OK, no OOB (negative control passed)
EXIT=0

Both binaries were re-built and re-run at packaging time and reproduce the above verbatim (ASan report addresses/PIDs differ per run, as expected).

Impact

Deserializing an untrusted TensorRT engine that embeds a crafted Region_TRT plugin blob causes an out-of-bounds heap READ during IRuntime::deserializeCudaEngine(). Consequences include denial of service (crash on a guard page / ASan-equivalent fault) and potential information disclosure (out-of-bounds heap contents copied into plugin state). Engine files are commonly treated as passively loadable model artifacts and shared across trust boundaries (model zoos, CI pipelines, inference services), so this is reachable by any actor able to supply an engine file.

Suggested fix

Validate every serialized count against the bytes remaining in the blob before allocating or looping β€” e.g. compute remaining = (a + length) - d and require n * stride <= remaining prior to the read loops, or bound each read<>() against a + length. The trailing PLUGIN_VALIDATE(d == a + length) check must be made non-trailing (checked incrementally, not only at the end).

Deduplication

  • Distinct plugin / file / function / struct (regionPlugin.cpp, Region::Region, struct softmaxTree) from the other findings in this audit: embLayerNorm, DecodeBbox3D, and PriorBox plugin OOB reads. The shared root-cause class (unchecked serialized counts feeding read<>() with a trailing-only length check) recurs across TensorRT plugins, but each is a separate code path with a separately triggered blob layout.
  • No public CVE currently attributes an OOB read to Region::Region / softmaxTree deserialization in TensorRT at the time of writing.
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support