YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ExecuTorch Method::get_inputs(): out-of-bounds read via unvalidated ExecutionPlan.inputs value-index
Conclusion
CONFIRMED (real ASan SEGV). A structurally-valid .pte whose
ExecutionPlan.inputs[i] holds an arbitrary value-table index causes an
out-of-bounds read of the runtime values_ array inside
executorch::runtime::Method::get_inputs()
(runtime/executor/method.cpp:1436). The index comes from
Method::get_input_index() (method.cpp:1801), which returns
serialization_plan_->inputs()->Get(i) without validating it against the
number of values β and get_inputs() uses it to index values_ directly,
bypassing the ET_CHECK bounds guard that Method::get_value() applies.
The bug persists under the strongest verification mode
(Program::Verification::InternalConsistency, i.e. full flatbuffers::Verifier
validate_program()), because neither the flatbuffers verifier norvalidate_program()range-checks theinputs/outputsindex vectors against the values table. This is an application-logic gap, not a verification-configuration issue.
- Target:
pytorch/executorch(ExecuTorch C++ runtime), categoryexecutorch - Commit verified:
a6d812a082df57898b8608f56c867140cc9da32c; line 1436 identical in livemain(fetched 2026-07-16 via raw.githubusercontent.com). - Component:
runtime/executor/method.cppβMethod::get_inputs()/Method::get_input_index() - Class: CWE-125 (Out-of-bounds Read)
- Impact: DoS (SEGV) and potential information disclosure (adjacent memory surfaced to caller as EValue payloads) on a public, documented runtime API when handed an untrusted model.
Root cause
runtime/executor/method.cpp:
// get_inputs(): OOB β indexes values_ directly, no bounds check
for (size_t i = 0; i < n_input; ++i) {
input_evalues[i] = values_[get_input_index(i)]; // line 1436
...
}
size_t Method::get_input_index(size_t i) const {
ET_CHECK_MSG(i < inputs_size(), ...); // only checks loop index i
return static_cast<size_t>(serialization_plan_->inputs()->Get(i)); // UNVALIDATED value index
}
Contrast the guarded accessors, which route the same index through
get_value() / mutable_value():
const EValue& Method::get_value(size_t i) const {
ET_CHECK_MSG(i < n_value_, ...); // bounds check present here
return values_[i];
}
get_input(i) (singular), set_input, MoveCall, FreeCall, etc. all go
through get_value/mutable_value and are bounds-checked. get_inputs()
(plural) is the one path that indexes values_ directly, so a malicious
inputs[i] is dereferenced out of bounds.
validate_program() (runtime/executor/program_validation.cpp) validates each
value's tensor and each TensorList item index, but never validates the
plan-level inputs()/outputs() index vectors β so InternalConsistency does
not catch this.
Proof of concept
build_pte.cppβ emits a minimal program:values=[Int(0)](sovalues_has exactly one element, index 0),inputs=[0x11111111], one chain with a singleFreeCall(value_index=0)(letsMethod::init()complete without any operator registration), empty delegates. Fully structurally valid.harness.cppβProgram::load()(default Minimal) βload_method("forward")βMethod::get_inputs(buf, 8).harness_verify.cppβ identical butProgram::load(..., InternalConsistency).build.shβ compiles the ExecuTorch runtime sources + harness with-fsanitize=address.
Crash (verbatim, default Minimal verification)
[harness] load_method OK. inputs_size=1. Calling get_inputs() (expected OOB read here)...
AddressSanitizer:DEADLYSIGNAL
==582470==ERROR: AddressSanitizer: SEGV on unknown address 0x55e6641fd328 ...
==582470==The signal is caused by a READ memory access.
#0 ... in executorch::runtime::EValue::EValue(EValue const&) runtime/core/evalue.h:162:55
#1 ... in executorch::runtime::EValue::operator=(EValue const&) runtime/core/evalue.h:180:13
#2 ... in executorch::runtime::Method::get_inputs(EValue*, unsigned long) runtime/executor/method.cpp:1436:22
#3 ... in main getinputs_oob/harness.cpp:67:20
SUMMARY: AddressSanitizer: SEGV runtime/core/evalue.h:162:55 in EValue::EValue(EValue const&)
Same crash under full InternalConsistency verification
harness_verify_asan malicious.pte β identical SEGV at method.cpp:1436,
proving the malicious file passes VerifyProgramBuffer + validate_program().
Negative control
Same generator with inputs[0]=0x0 (benign.pte, valid index): both harnesses
print get_inputs returned 0x0 (NO CRASH) and exit cleanly. Only the
out-of-range index triggers the fault.
Distinctness / dedup
Distinct from all prior session/public ExecuTorch findings:
- Not a NULL deref (compilespec / backend-delegate-data / FlatTensor).
- Not a backend-delegate bug (XNNPACK / Vulkan / QNN / Ethos-U / WebGPU).
- Distinct from
huntr-poc-executorch-oob(the pybindings Minimal-verification-downgrade / flatbuffer-verifier-bypass OOB): that bug relies on the Verifier being skipped and an inflated vector length prefix. This bug is a semantic index-range gap inMethod::get_inputs()that the Verifier is not designed to catch and that survives InternalConsistency. - File/line:
runtime/executor/method.cpp:1436/:1801(core executor).
- Downloads last month
- -