Null Pointer Dereference in ExecuTorch MethodMeta::get_backend_name() via Missing BackendDelegate.id (.pte)
Target: ExecuTorch 1.3.1 (.pte, huntr Model File Vulnerability program)
Severity: Low-Medium (Denial of Service)
CWE: CWE-476 (NULL Pointer Dereference)
Component: runtime/executor/method_meta.cpp
Authentication Required: No β only requires a victim application to load an attacker-supplied .pte file and call a public API.
Summary
MethodMeta::get_backend_name() is a public API for enumerating the backend names used by a loaded method (e.g. for logging or diagnostics). It dereferences BackendDelegate.id without checking it for null, even though id is an ordinary (non-required) field in the schema. A .pte file with a well-formed delegates array containing an entry whose id is omitted crashes any process that calls get_backend_name() for that entry's index.
This is a distinct bug from the previously identified uses_backend() null-derefs (same file, same underlying BackendDelegate.id field, but a different function and call site) β confirmed independently below using a harness that calls only get_backend_name(), with zero calls to any other MethodMeta method, ruling out any possibility this is merely an artifact of a different bug being triggered first.
Vulnerability Details
schema/program.fbs:
table BackendDelegate {
id: string; // not required
processed: BackendDelegateDataReference;
compile_specs: [CompileSpec];
}
runtime/executor/method_meta.cpp (pristine 1.3.1 source, lines 424β433):
Result<const char*> MethodMeta::get_backend_name(size_t index) const {
const auto count = num_backends();
ET_CHECK_OR_RETURN_ERROR(
index < count,
InvalidArgument,
"Index %zu out of range. num_backends: %zu",
index,
count);
return s_plan_->delegates()->Get(index)->id()->c_str(); // <-- crash site, line 432
}
The bounds check (index < count) only validates the index, not the content of the BackendDelegate at that index. A BackendDelegate entry with no id is fully legal per the schema, so id() returns nullptr and ->c_str() crashes.
Steps to Reproduce
Environment
Linux x86-64, ExecuTorch 1.3.1 pristine source, clang-16, CMake, Ninja. No authentication, no host access.
1. Build ExecuTorch with sanitizers
Same build as REPORT-01/03 Step 1 β the default target set includes runtime/executor/method_meta.cpp.
2. Build the isolated PoC harness (poc/harness_isolated_get_backend_name.cpp, included in this report)
This harness deliberately calls only get_backend_name() β no uses_backend(), no other MethodMeta accessor β to prove this bug is independent of any other finding in this codebase:
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size) {
// ... load Program under InternalConsistency verification ...
auto meta = p.method_meta(name.get());
if (meta.ok()) {
auto& m = meta.get();
size_t nb = m.num_backends();
for (size_t bi = 0; bi < nb; ++bi) {
(void)m.get_backend_name(bi); // ONLY this call
}
}
}
export ET_PARENT=/path/to/parent-of-executorch
C10_INC="$ET_SRC/runtime/core/portable_type/c10"
INCLUDES="-I$ET_PARENT -I$ET_BUILD -I$ET_BUILD/schema/include -I$ET_BUILD/extension/flat_tensor/include -I$ET_BUILD/third-party/flatc_ep/include -I$C10_INC"
clang++-16 -std=c++17 -fsanitize=fuzzer,address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all \
$INCLUDES -DFLATBUFFERS_MAX_ALIGNMENT=1024 -DC10_USING_CUSTOM_GENERATED_MACROS \
-c poc/harness_isolated_get_backend_name.cpp -o harness.o
clang++-16 -fsanitize=fuzzer,address,undefined -o poc_harness harness.o \
"$ET_BUILD/extension/data_loader/libextension_data_loader.a" \
"$ET_BUILD/libexecutorch_core.a"
3. PoC file
poc/poc_get_backend_name_null.pte (728 bytes, included in this report β sha256 7675e37734409f4ec13bc31bd21963b085312f65c33dfc583cd0d1521c22be0a) is a well-formed .pte file containing an ExecutionPlan whose delegates array has at least one BackendDelegate entry with id omitted.
4. Trigger the crash
export ASAN_OPTIONS="abort_on_error=1:symbolize=0"
export UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=0"
./poc_harness -timeout=5 -runs=0 poc/poc_get_backend_name_null.pte
Expected result (secure behavior)
get_backend_name() should return a clean Error (e.g. InvalidProgram) for a delegate entry with no id, since there is no valid name to return.
Actual result β verified in isolation (harness calls ONLY get_backend_name, no other MethodMeta method)
Running: poc/poc_get_backend_name_null.pte
runtime/executor/method_meta.cpp:432:50: runtime error: member call on null pointer of type 'flatbuffers::String'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior runtime/executor/method_meta.cpp:432:50 in
==<pid>== ERROR: libFuzzer: deadly signal
Reproduced 3/3 identical runs, in isolation (re-verified live for this report using a harness that calls no other MethodMeta method):
run 1: runtime/executor/method_meta.cpp:432:50: runtime error: member call on null pointer of type 'flatbuffers::String'
run 2: (identical)
run 3: (identical)
This isolation test is the key piece of evidence distinguishing this from a previously-identified uses_backend() bug in the same file: it proves get_backend_name() crashes on this exact field independent of any other code path being exercised first.
Impact
Who is affected: Any application calling MethodMeta::get_backend_name() to enumerate backend names β a normal diagnostic/logging pattern for applications that report which backends a loaded model uses.
What the attacker can do: Cause a deterministic crash by supplying a .pte file with a BackendDelegate entry that has no id β legal per schema, undetected by both of ExecuTorch's verification levels.
What's at risk: Availability only.
Why not Critical: Controlled null-pointer dereference, no memory corruption or code execution.
Suggested Remediation
Result<const char*> MethodMeta::get_backend_name(size_t index) const {
const auto count = num_backends();
ET_CHECK_OR_RETURN_ERROR(
index < count,
InvalidArgument,
"Index %zu out of range. num_backends: %zu",
index,
count);
auto delegate = s_plan_->delegates()->Get(index);
ET_CHECK_OR_RETURN_ERROR(
delegate != nullptr && delegate->id() != nullptr,
InvalidProgram,
"Backend delegate %zu or its id is null",
index);
return delegate->id()->c_str();
}
Design recommendation: this is the second function in method_meta.cpp found to independently dereference BackendDelegate.id without a null check (the other being uses_backend()). A single shared helper β e.g. Result<const char*> get_delegate_id(size_t index) β used by both functions would prevent a third such call site from repeating this pattern in the future.
A regression test should build an ExecutionPlan.delegates entry with id omitted and assert get_backend_name() returns a clean Error rather than crashing.
Files Included in This Report
poc/poc_get_backend_name_null.pteβ the 728-byte PoC file (sha2567675e37734409f4ec13bc31bd21963b085312f65c33dfc583cd0d1521c22be0a)poc/harness_isolated_get_backend_name.cppβ the isolated harness proving this bug independent of any otherMethodMetacode path
huntr Submission Note
Per the huntr MFV program's submission requirements, this PoC needs to be uploaded to a public HuggingFace repository before filing. poc/poc_get_backend_name_null.pte is ready for that upload.