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.

DetectionLayer_TRT plugin deserialize heap-buffer-overflow OOB read

Target: NVIDIA TensorRT-OSS plugin library โ€” https://github.com/NVIDIA/TensorRT Component: plugin/detectionLayerPlugin/detectionLayerPlugin.cpp โ€” DetectionLayer::DetectionLayer(void const* data, size_t length) (~line 236) Helper: plugin/common/plugin.h โ€” read<>() (lines 100-108) Class: CWE-125 Out-of-bounds Read (heap-buffer-overflow, host-side) License: Apache-2.0


Summary

The DetectionLayer_TRT plugin's deserialization constructor reads a fixed 24 bytes from an attacker-controlled serialized blob via six unconditional read<T>() calls before performing its single length/bound check. read<>() is an unchecked std::memcpy(sizeof(OutType)) that advances the cursor with no bounds validation. There is no up-front length validation โ€” a blob shorter than 24 bytes causes the read<>() calls to walk past the end of the heap allocation holding the serialized plugin field before the guard ever executes.

This is reached purely host-side (no GPU required) when IRuntime::deserializeCudaEngine() parses an attacker-supplied .plan/.engine containing a DetectionLayer_TRT layer.


Trigger path in real TensorRT

IRuntime::deserializeCudaEngine(attacker .plan/.engine)
  -> engine reader slices the plugin field blob (data, length) from the plan
  -> DetectionLayerPluginCreator::deserializePlugin(name, data, length)   (~line 102)
       -> new DetectionLayer(data, length)                                <-- vulnerable

data / length are copied verbatim out of the plan file; length is the attacker-declared byte count of the plugin field region.


Root cause (verbatim from detectionLayerPlugin.cpp, ~line 236)

DetectionLayer::DetectionLayer(void const* data, size_t length)
{
    const char *d = reinterpret_cast<const char*>(data), *a = d;
    mNbClasses      = read<int32_t>(d);   // +4
    mKeepTopK       = read<int32_t>(d);   // +4
    mScoreThreshold = read<float>(d);     // +4
    mIOUThreshold   = read<float>(d);     // +4
    mMaxBatchSize   = read<int32_t>(d);   // +4
    mAnchorsCnt     = read<int32_t>(d);   // +4   -> 24 bytes consumed unconditionally
    PLUGIN_VALIDATE(d == a + length);     // bound check -- runs TOO LATE
    ...
}

The unchecked primitive (plugin/common/plugin.h, lines 100-108):

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));  // no bound check
    buffer += sizeof(OutType);
    return val;
}

getSerializationSize() for this plugin is 2*int32 + 2*float + 2*int32 == 24, but the deserializer never checks that length >= 24 before consuming the 24 bytes.

Contrast: the sibling ROIAlign plugin is NOT vulnerable

plugin/roiAlignPlugin/roiAlignPluginLegacy.cpp guards before any read<>():

PLUGIN_VALIDATE(length == kSERIALIZATION_SIZE);   // up-front length check

DetectionLayer omits this up-front check, which is the bug.


PoC

detectionLayer_harness.cpp is a faithful standalone ASan harness reproducing the exact deserialize path: read<>() is copied verbatim from plugin/common/plugin.h, and the DetectionLayer(data, length) constructor body is reproduced line-for-line. The blob is heap-allocated at the attacker-declared length (exactly as TensorRT's engine reader slices the plugin-field bytes from the plan), so ASan's redzone sits immediately after the declared length โ€” modeling the real heap boundary.

Build

clang++ -std=c++17 -O0 -g -fsanitize=address -fno-omit-frame-pointer \
    detectionLayer_harness.cpp -o detectionLayer_asan

Run

./detectionLayer_asan 8    # POSITIVE: short blob  -> heap-buffer-overflow READ
./detectionLayer_asan 23   # BOUNDARY: one byte short -> heap-buffer-overflow READ
./detectionLayer_asan 24   # NEGATIVE CONTROL: well-formed -> constructs cleanly, no error

Captured evidence (verbatim)

POSITIVE (length=8)

[harness] DetectionLayer_TRT deserialize: length=8, well-formed=24
=================================================================
==428999==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b76a69e0018 at pc 0x556c3e7b55cf bp 0x7fff838e5000 sp 0x7fff838e47c0
READ of size 4 at 0x7b76a69e0018 thread T0
    #0 0x556c3e7b55ce in __asan_memcpy
    #1 0x556c3e7fdcb6 in float nvinfer1::plugin::read<float, unsigned char>(unsigned char const*&) detectionLayer_harness.cpp:62:5
    #2 0x556c3e7fd904 in DetectionLayer::DetectionLayer(void const*, unsigned long) detectionLayer_harness.cpp:97:27
    #3 0x556c3e7fd278 in main detectionLayer_harness.cpp:124:24
0x7b76a69e0018 is located 0 bytes after 8-byte region [0x7b76a69e0010,0x7b76a69e0018)
allocated by thread T0 here:
    #0 0x556c3e7b7978 in malloc
    #1 0x556c3e7fd159 in main detectionLayer_harness.cpp:115:43
SUMMARY: AddressSanitizer: heap-buffer-overflow in __asan_memcpy

The overflow fires at deserialize step 3 (read<float> for mScoreThreshold), which is the first read that crosses the 8-byte boundary.

BOUNDARY (length=23, one byte short of the 24-byte serialization size)

==429051==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b8f9e5e0057 ...
0x7b8f9e5e0057 is located 0 bytes after 23-byte region [0x7b8f9e5e0040,0x7b8f9e5e0057)
SUMMARY: AddressSanitizer: heap-buffer-overflow in __asan_memcpy

NEGATIVE CONTROL (length=24, well-formed getSerializationSize)

[harness] DetectionLayer_TRT deserialize: length=24, well-formed=24
[harness] constructed OK: nbClasses=1145258561 keepTopK=1212630597 maxBatch=1414746705 anchors=1482118741

No ASan error; the guard PLUGIN_VALIDATE(d == a + length) passes and construction completes. This confirms the harness only faults on under-length (attacker) input.


Impact

An attacker who can get a victim to load a malicious .plan/.engine (a common distribution artifact for TensorRT-accelerated models) triggers an out-of-bounds heap read during engine deserialization, before any inference runs and without a GPU. Depending on heap layout this is an information-disclosure / crash primitive (DoS), reachable entirely from the untrusted model file.

Suggested fix

Add an up-front length check mirroring the ROIAlign sibling, e.g. PLUGIN_VALIDATE(length == getSerializationSize()); (24 bytes) before the first read<>() call.


Dedup note

Distinct from all previously-covered TensorRT plugin deserialize OOBs (priorbox, decodebbox3d, emblayernorm, region, PillarScatter, FlattenConcat, RPROI, GridAnchor, ProposalLayer). detectionLayerPlugin is a separate plugin/source file not among them. Confirmed non-vulnerable siblings during the hunt: roiAlignPluginLegacy (validates length == kSERIALIZATION_SIZE up front), groupNormalizationPlugin and scatterElementsPluginLegacy (use length-checked deserialize_value). No public CVE currently tracks the DetectionLayer_TRT deserializer.

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