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.

DecodeBbox3DPlugin deserialize() unbounded readFromBuffer loops driven by attacker-controlled mNumClasses cause heap OOB read

Summary

DecodeBbox3DPlugin::DecodeBbox3DPlugin(void const* data, size_t length) in TensorRT-OSS deserializes a native plugin blob from an engine plan file. It reads a fixed 52-byte header of 13 scalar fields with plugin::readFromBuffer<>(), a raw pointer dereference that performs no bounds check against length. One header field, mNumClasses (int32), is then used directly as the trip count for two unbounded read loops. A crafted engine plan whose inline DecodeBbox3DPlugin blob declares a large mNumClasses but is physically short triggers a heap-buffer-overflow read past the end of the deserialization buffer. The only bounds check (PLUGIN_VALIDATE(d == data + length)) runs after both loops โ€” after the OOB reads have already occurred.

Target

  • Project: NVIDIA TensorRT-OSS (NVIDIA/TensorRT)
  • Version: 11.1.0.106 (VERSION file), commit a892d22267d9cd2dedc1a0893e6892ac901f6d3d
  • Vulnerable file: plugin/decodeBbox3DPlugin/decodeBbox3D.cpp, lines 81-112 (ctor)
  • Helper file: plugin/common/templates.h, lines 41-47 (readFromBuffer<>)
  • Plugin registered via REGISTER_TENSORRT_PLUGIN(DecodeBbox3DPluginCreator); entry point DecodeBbox3DPluginCreator::deserializePlugin (decodeBbox3D.cpp:481).

Reachability

IRuntime::deserializeCudaEngine(plan, planLength)          (closed TensorRT core)
  -> IPluginCreator::deserializePlugin(name, serialData, serialLength)
     (DecodeBbox3DPluginCreator, registered via REGISTER_TENSORRT_PLUGIN)
     -> new DecodeBbox3DPlugin(serialData, serialLength)    <-- vulnerable ctor

serialData / serialLength are copied verbatim out of the attacker-controlled engine plan file (the plugin's serialized blob is stored inline in the engine's layer table). Nothing in the closed core validates that the blob's field values are self-consistent with the number of bytes physically present before this constructor runs. Loading an untrusted .engine/.plan file โ€” a documented deployment pattern for serving prebuilt engines โ€” reaches this path.

Root cause

The deserialization helper (verbatim, plugin/common/templates.h:41-47):

// Helper function for deserializing plugin
template <typename ValType, typename BufferType>
ValType readFromBuffer(BufferType const*& buffer)
{
    auto val = *toPointer<ValType const>(buffer);   // raw deref, NO bounds check
    buffer += sizeof(ValType);
    return val;
}

The constructor (verbatim, plugin/decodeBbox3DPlugin/decodeBbox3D.cpp:81-112):

DecodeBbox3DPlugin::DecodeBbox3DPlugin(void const* data, size_t length)
{
    PLUGIN_VALIDATE(data != nullptr);
    auto const* d = reinterpret_cast<uint8_t const*>(data);
    mMinXRange = readFromBuffer<float>(d);
    mMaxXRange = readFromBuffer<float>(d);
    mMinYRange = readFromBuffer<float>(d);
    mMaxYRange = readFromBuffer<float>(d);
    mMinZRange = readFromBuffer<float>(d);
    mMaxZRange = readFromBuffer<float>(d);
    mNumDirBins = readFromBuffer<int32_t>(d);
    mDirOffset = readFromBuffer<float>(d);
    mDirLimitOffset = readFromBuffer<float>(d);
    mScoreThreashold = readFromBuffer<float>(d);
    mNumClasses = readFromBuffer<int32_t>(d);      // attacker-controlled
    mFeatureH = readFromBuffer<int32_t>(d);
    mFeatureW = readFromBuffer<int32_t>(d);

    mAnchorBottomHeight.resize(mNumClasses);
    for (int32_t i = 0; i < mNumClasses; i++)
    {
        mAnchorBottomHeight[i] = readFromBuffer<float>(d);   // OOB read
    }

    mAnchors.resize(mNumClasses * 2 * 4);
    for (int32_t i = 0; i < mNumClasses * 2 * 4; i++)
    {
        mAnchors[i] = readFromBuffer<float>(d);              // OOB read
    }

    PLUGIN_VALIDATE(d == reinterpret_cast<uint8_t const*>(data) + length);  // too late
}

The 13-field header is exactly 13 * 4 = 52 bytes. mNumClasses is read at offset 40 and never validated. By supplying a blob that is exactly the 52-byte header (no payload) with a large mNumClasses, the very first mAnchorBottomHeight loop iteration reads a float at data + 52 โ€” one byte past the end of the heap allocation holding the blob. length is never consulted during the reads; the trailing PLUGIN_VALIDATE bounds check executes only after both loops have already read out of bounds.

Proof of Concept

poc/decodeBbox3D_harness.cpp reproduces the verbatim constructor body from decodeBbox3D.cpp:81-112 plus the verbatim readFromBuffer/toPointer helpers from common/templates.h. PLUGIN_VALIDATE is stubbed to throw (matching the real macro's failure semantics). A malicious plugin blob is built as exactly the 52-byte header (13 scalar fields, no payload) with mNumClasses=4096, heap-allocated at exactly its length via malloc (ASAN-tracked). The constructor's first mAnchorBottomHeight loop iteration reads a float at data+52, one byte past the 52-byte allocation, triggering an ASAN heap-buffer-overflow READ.

Build & run:

clang++ -std=c++17 -O0 -g -fsanitize=address -fno-omit-frame-pointer \
        decodeBbox3D_harness.cpp -o decode_asan
./decode_asan          # attack
./decode_asan neg      # negative control

Negative control: a well-formed 124-byte blob with mNumClasses=2 and a matching 18-float payload (2 anchorBottomHeight + 2*8 anchors) deserializes cleanly with no ASAN error and passes the trailing PLUGIN_VALIDATE.

Captured evidence (verbatim)

[attack] blob length = 52 bytes (header only), mNumClasses=4096
[attack] entering DecodeBbox3DPlugin ctor; expect ASAN heap OOB read...
=================================================================
==322126==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7bb6537e0054 at pc 0x5619f3e7daf7 bp 0x7ffc55a73270 sp 0x7ffc55a73268
READ of size 4 at 0x7bb6537e0054 thread T0
    #0 0x5619f3e7daf6 in float nvinfer1::plugin::readFromBuffer<float, unsigned char>(unsigned char const*&) decodeBbox3D_harness.cpp:67:16
    #1 0x5619f3e7d6e8 in DecodeBbox3DPlugin::DecodeBbox3DPlugin(void const*, unsigned long) decodeBbox3D_harness.cpp:111:38
    #2 0x5619f3e7c659 in main decodeBbox3D_harness.cpp:167:24

0x7bb6537e0054 is located 0 bytes after 52-byte region [0x7bb6537e0020,0x7bb6537e0054)
allocated by thread T0 here:
    #0 0x5619f3e369c8 in malloc
    #1 0x5619f3e7c93e in buildBlob(int, unsigned long, unsigned long*) decodeBbox3D_harness.cpp:129:42
    #2 0x5619f3e7c5cf in main decodeBbox3D_harness.cpp:163:33

SUMMARY: AddressSanitizer: heap-buffer-overflow decodeBbox3D_harness.cpp:67:16 in float nvinfer1::plugin::readFromBuffer<float, unsigned char>(unsigned char const*&)
==322126==ABORTING

--- Negative control ---
[neg] blob length = 124 bytes, mNumClasses=2
[neg] deserialized OK, no OOB (expected)

(The address/PID/PC values differ run to run; the fault site, allocation site, and access size are stable.)

Impact

Heap out-of-bounds read of attacker-influenced size during engine deserialization. Consequences: information disclosure (adjacent heap contents are read into plugin fields and can subsequently influence kernel behavior/outputs), and denial of service (ASAN abort in a hardened build; SIGSEGV when the read walks off a mapped page for a sufficiently large mNumClasses). Trigger requires only that a victim deserialize an untrusted engine plan โ€” a common pattern when distributing prebuilt TensorRT engines.

Suggested fix

Validate remaining buffer length before every read. Before the loops, require length >= 52 and length == 52 + (mNumClasses + mNumClasses*2*4) * sizeof(float) (and reject negative/oversized mNumClasses) before performing any element reads โ€” i.e., move the size self-consistency check ahead of the loops rather than after them, and make readFromBuffer bounds-aware against length.

Dedup note

Distinct from the prior TensorRT plugin OOB finding in embLayerNormPlugin (convertAndCopy / WeightsWithConversion::convertAndCopy bulk copy driven by mLd/vocab sizes). This is a different plugin (decodeBbox3DPlugin), a different helper (per-element readFromBuffer<> loop vs. bulk convertAndCopy), and a different attacker-controlled field (mNumClasses vs mLd/vocab sizes). Also distinct from the earlier TensorRT ONNX path-traversal and volume int-overflow findings. No CVE currently maps to DecodeBbox3DPlugin deserialization.

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