YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ExecuTorch .pte loader: NULL-pointer dereference in Program::get_backend_delegate_data() (missing backend_delegate_data field)
Summary
A structurally-valid, fully-verified ExecuTorch .pte model triggers a NULL-pointer
dereference (SIGSEGV, READ at address 0x0) the moment a method is loaded via
Program::load_method(). The crash occurs in
executorch::runtime::Program::get_backend_delegate_data()
(runtime/executor/program.cpp:538-546), which calls data_list->size() on the
result of the generated flatbuffers accessor backend_delegate_data() without a
null check.
- Target:
pytorch/executorchruntime (.ptemodel loader) - Commit:
a6d812a082df57898b8608f56c867140cc9da32c - Component:
runtime/executor/program.cppโProgram::get_backend_delegate_data() - Class: CWE-476 (NULL Pointer Dereference)
- Impact: Denial of Service when loading an untrusted/attacker-supplied
.pte. The crash fires even underProgram::Verification::InternalConsistencyโ the strongest verification mode ExecuTorch offers, which the malicious file passes cleanly before crashing at method-load time.
Root cause
runtime/executor/program.cpp:538-546:
Error Program::get_backend_delegate_data(
size_t index,
const void** out_data,
size_t* out_size) const {
const auto* data_list =
static_cast<const executorch_flatbuffer::Program*>(internal_program_)
->backend_delegate_data(); // nullptr when the field is absent
ET_CHECK_OR_RETURN_ERROR(
index < data_list->size(), // <-- data_list->size() derefs nullptr
InvalidArgument,
"index %zu >= list size %" PRIu32,
index,
data_list->size());
...
Program.backend_delegate_data is not marked (required) in
schema/program.fbs (line 506):
// schema/program.fbs (line ~506)
table Program {
...
// Tables of constant data, used for keeping the constant data away from the
// program.
backend_delegate_data:[BackendDelegateInlineData]; // NOT (required)
...
}
Because the field is optional, a legal FlatBuffer may omit it entirely. The
flatc-generated accessor Program::backend_delegate_data() then returns nullptr,
and the subsequent data_list->size() call
(third-party/flatbuffers/include/flatbuffers/vector.h:164) dereferences a null
Vector pointer, producing a SEGV read at 0x0.
Trigger path
The malicious file contains a BackendDelegate whose processed reference is
present with location = INLINE, index = 0, while the top-level
Program.backend_delegate_data vector is absent:
Program::load_method("forward")
-> Method::init() method.cpp:971
-> BackendDelegate::Init() method.cpp:97
-> BackendDelegate::GetProcessedData() [INLINE case] method.cpp:202
-> Program::get_backend_delegate_data(0) program.cpp:541
-> data_list->size() // data_list == nullptr => SEGV read @ 0x0
Proof of Concept
build_pte.cppbuilds the malicious.pteusing flatc-generated ExecuTorch schema headers: oneExecutionPlannamed"forward"containing oneBackendDelegate { id = "TESTBACKEND", processed = BackendDelegateDataReference { location = INLINE, index = 0 } }, while the top-levelProgram.backend_delegate_datavector is omitted (serialized offset0).build_pte_benign.cppbuilds an otherwise identicalbenign.ptethat DOES include abackend_delegate_datavector with one entry (negative control).harness.cppis compiled with-fsanitize=addressagainst the real ExecuTorch runtime sources. It registers a no-op backend"TESTBACKEND", loads the file withProgram::load(loader, Program::Verification::InternalConsistency)(which PASSES), then callsprogram.load_method("forward").
Build/run:
bash build.sh # builds build_pte + ASan harness
./build_pte malicious.pte
ASAN_OPTIONS=detect_leaks=0 ./harness_asan malicious.pte # SEGV
./build_pte_benign benign.pte
ASAN_OPTIONS=detect_leaks=0 ./harness_asan benign.pte # no crash (control)
Captured evidence (verbatim)
Malicious input (malicious.pte, backend_delegate_data omitted):
[harness] Program::load(InternalConsistency) ...
[harness] Program::load() OK (passed full verification). Loading method "forward" ...
AddressSanitizer:DEADLYSIGNAL
==1961067==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5643edbd2475 bp 0x7ffe118bdf80 sp 0x7ffe118bdf60 T0)
==1961067==The signal is caused by a READ memory access.
==1961067==Hint: address points to the zero page.
#0 0x5643edbd2475 in flatbuffers::Vector<flatbuffers::Offset<executorch_flatbuffer::BackendDelegateInlineData>, unsigned int>::size() const .../third-party/flatbuffers/include/flatbuffers/vector.h:164:44
#1 0x5643edbcf4bd in executorch::runtime::Program::get_backend_delegate_data(unsigned long, void const**, unsigned long*) const .../runtime/executor/program.cpp:541:3
#2 0x5643edbc0b3a in executorch::runtime::BackendDelegate::GetProcessedData(executorch_flatbuffer::BackendDelegate const&, executorch::runtime::Program const*) .../runtime/executor/method.cpp:202:30
#3 0x5643edbb974d in executorch::runtime::BackendDelegate::Init(executorch_flatbuffer::BackendDelegate const&, executorch::runtime::Program const*, executorch::runtime::BackendInitContext&, executorch::runtime::BackendDelegate*) .../runtime/executor/method.cpp:97:45
#4 0x5643edbaa326 in executorch::runtime::Method::init(executorch_flatbuffer::ExecutionPlan*, executorch::runtime::NamedDataMap const*, executorch::runtime::LoadBackendOptionsMap const*) .../runtime/executor/method.cpp:971:19
#5 0x5643edba90bc in executorch::runtime::Method::load(...) .../runtime/executor/method.cpp:862:22
#6 0x5643edbcdd11 in executorch::runtime::Program::load_method(...) .../runtime/executor/program.cpp:392:10
#7 0x5643edba07ed in main .../bug_backend_delegate_data/harness.cpp:82:15
#8 0x7ff10773bf76 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29f76)
#9 0x7ff10773c026 in __libc_start_main
#10 0x5643edab5420 in _start
==1961067==Register values:
rax = 0x0000000000000000 ... rdi = 0x0000000000000000 ...
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV .../third-party/flatbuffers/include/flatbuffers/vector.h:164:44 in flatbuffers::Vector<flatbuffers::Offset<executorch_flatbuffer::BackendDelegateInlineData>, unsigned int>::size() const
==1961067==ABORTING
Negative control (benign.pte, backend_delegate_data present with one entry) โ
no crash; load proceeds past the delegate to an unrelated later error:
[harness] Program::load(InternalConsistency) ...
[harness] Program::load() OK (passed full verification). Loading method "forward" ...
E 00:00:00.000266 executorch:method.cpp:987] No chains
load_method returned error (no crash): 0x23
Distinct from prior findings
This is a separate defect from the other known ExecuTorch delegate-loader null-dereferences:
- NOT the CompileSpec
key/valuenullderef (method.cpp,PopulateCompileSpecs). - NOT the
processed-absent nullderef (GetProcessedData,method.cpp:197,processed->location()). In this PoCprocessedIS present (location = INLINE, index = 0); the crash occurs one call deeper, in a different source file (program.cpp), on a missing null-check of a different, Program-level schema field (backend_delegate_data). - NOT the
.ptdFlatTensorDataMapnullderef.
Suggested fix
Null-check data_list in get_backend_delegate_data() before use:
const auto* data_list = ...->backend_delegate_data();
ET_CHECK_OR_RETURN_ERROR(
data_list != nullptr, NotFound,
"Program has no backend_delegate_data but an INLINE delegate references it");
ET_CHECK_OR_RETURN_ERROR(index < data_list->size(), InvalidArgument, ...);
- Downloads last month
- -