ExecuTorch .pte + .ptd external-constant heap out-of-bounds read β PoC
Security research artifact. Do not load these files with a vulnerable ExecuTorch build outside a sandbox.
Reported through huntr (Model File Formats / AI-ML) under coordinated disclosure by Roman Arce Bran β Cyfra Tech Solutions (Costa Rica).
- Target:
pytorch/executorch,main@55d693b1655314526d25436754d83b9554049540(2026-07-27) - CWE-125 out-of-bounds read β CWE-200 information disclosure, plus a remote worker crash
- Entry point: the documented public API
executorch::extension::Module(model_path, {data_files...})
The defect in one line
The .ptd declares the tensor's size twice and independently β once as NamedData.tensor_layout
(scalar_type/sizes/dim_order β nbytes()) and once as DataSegment.size (the bytes
actually allocated and read). The runtime never compares them, so a 4 KiB tensor can be backed by a
16-byte heap allocation.
getTensorDataPtr() (runtime/executor/tensor_parser_exec_aten.cpp:194) receives the correct byte
count as a parameter and uses it in every branch except the external-constant one
(lines 221-229), which returns data->buffer.data() unchecked.
Files
| file | what it is |
|---|---|
poc/poc_leak.{pte,ptd} |
4 KiB declared over a 16-byte segment β silent 4080-byte over-read |
poc/poc_leak16k.*, poc/poc_leak32k.*, poc/poc_leak64k.* |
larger exfiltration windows |
poc/poc_big.{pte,ptd} |
4 MiB declared β walks off the heap, SIGSEGV (denial of service) |
poc/poc_tiny.{pte,ptd} |
256 B declared β minimal ASAN witness |
poc/poc_nokernel.{pte,ptd} |
empty instruction list β no kernel runs at all; the runtime still hands the caller a Tensor with nbytes()=256 over a 16-byte allocation |
gen_poc.py |
builds every pair from the upstream schemas with upstream flatc β these are well-formed files, not fuzzer garbage |
poc_main.cpp, build.sh |
standalone driver (ASAN, -O2 -DNDEBUG) |
server_main.cpp, build_server.sh |
minimal model-serving service: loads an uploaded model over TCP and returns the output tensor |
attacker_client.py |
remote client β separate process, socket only, no access to the server's memory |
run_exfil_suite.sh, sweep_window.sh, aslr_analysis.py |
end-to-end exfiltration + measured ASLR-defeat harness |
ASAN-witness.txt, EXFIL-witness.txt |
captured evidence |
Measured impact
- 4080 β 65520 bytes of server heap delivered to the remote party that uploaded the model (the attacker picks the size)
- 228 verbatim copies of another tenant's session token in a single 32 KiB response, 5/5 runs
- ASLR: across 5 fresh victim processes, 2148/2148 base predictions matched the victim's real
/proc/self/mapsexactly β both the[heap]base and the PIE image base β from one 864-byte model - 4 MiB window β
SIGSEGV(exit 139), reproducible remote DoS of the worker
Reproduce
python3 gen_poc.py ./poc
./build_server.sh
./sweep_window.sh
POC=leak32k ./run_exfil_suite.sh 5
python3 aslr_analysis.py /tmp/cyfra_exfil 5
Access to this repository is gated and granted to protectai-bot for huntr triage.
- Downloads last month
- 7