| --- |
| license: other |
| tags: |
| - security |
| - bug-bounty |
| - proof-of-concept |
| - executorch |
| --- |
| |
| # ExecuTorch `.pte` β benign crash PoC (out-of-bounds read on `load_method`) |
| |
| **This repository contains a benign proof-of-concept only. It causes a crash (SIGBUS, |
| out-of-bounds read) and does not execute attacker-controlled code, install a payload, or |
| perform any harmful action.** It exists to demonstrate a memory-safety vulnerability to |
| [Protect AI / huntr](https://huntr.com) as part of a coordinated disclosure. |
| |
| ## What this is |
| |
| `poc_delegate_oob.pte` is a malformed ExecuTorch model file. When loaded by the standard |
| ExecuTorch C++ runtime under its **default** verification mode (`Verification::Minimal`), |
| it triggers an out-of-bounds read (SIGBUS) inside the XNNPACK backend delegate handoff. |
| |
| Root cause: `Program::get_backend_delegate_data()` (`runtime/executor/program.cpp`) returns |
| the inline delegate blob's FlatBuffer-declared length **without validating it against the |
| real program buffer**, unlike the sibling `get_constant_buffer_data()`, which does bound |
| its size. A forged length field (~2 GiB, real blob ~30 bytes) is handed to the XNNPACK |
| backend as `(pointer, forged_size)`; `XNNCompiler::compileModel` then reads far outside the |
| mapped buffer and crashes. |
|
|
| ## What it does NOT do |
|
|
| - No shellcode, no payload, no filesystem/network side effects. |
| - The only observable effect of loading this file is a process crash (SIGBUS). |
|
|
| ## Reproduction |
|
|
| ```python |
| from executorch.extension.pybindings import portable_lib as pl |
| |
| data = open("poc_delegate_oob.pte", "rb").read() |
| prog = pl._load_program_from_buffer(data, program_verification=pl.Verification.Minimal) |
| for i in range(prog.num_methods()): |
| prog.load_method(prog.get_method_name(i)) # crashes with SIGBUS here |
| ``` |
|
|
| `poc_delegate_oob.py` is the script used to craft `poc_delegate_oob.pte` from a valid |
| `.pte` (`valid.pte`, included for reference) and to reproduce the crash under both the |
| default `Verification::Minimal` (crashes) and `Verification::InternalConsistency` (clean |
| error), confirming the FlatBuffer verifier is the missing guard. |
|
|
| ### Fork-free reproduction (recommended) |
|
|
| `poc_delegate_oob.py`'s harness forks a child before loading, which is standard practice |
| for crash-testing but can invite a "is this just a fork/threading artifact?" question (Python |
| emits a benign `DeprecationWarning` about forking while multi-threaded β cosmetic, unrelated |
| to the crash). `standalone_crash.py` reproduces the identical crash with **no fork** β it |
| crafts the file and calls `load_method` directly in the main process: |
|
|
| ``` |
| python standalone_crash.py |
| # crashes immediately: Bus error (SIGBUS) |
| ``` |
|
|
| This is the same craft path and produces the same symbolicated backtrace |
| (`XNNCompiler::compileModel β Method::init β Program::load_method`) and the same fault |
| address (`Data Abort`, byte read, `== blob_base + attacker flatbuffer_offset`), confirming |
| the crash fires on the ordinary single-process load path a real embedding application uses. |
|
|
| ## Disclosure status |
|
|
| Reported via huntr's Model File Vulnerability program (format: ExecuTorch / `.pte`). |
| This repo is gated β access requests are manually reviewed β per huntr's MFV PoC |
| submission requirements. |
|
|