# 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): ```cpp Result get_named_data( executorch::aten::string_view key, const flatbuffers::Vector>* named_data, const flatbuffers::Vector>* 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: ```cpp 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))); } } ``` ```bash 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 ```bash 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' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior extension/flat_tensor/flat_tensor_data_map.cpp:61:50 in ==== 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' 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 ```cpp 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 (sha256 `9fcfd7bc5d389d0c86e93fc5a847bb5ee537be8ffaa664059d1afbf3e8f97f9c`) - `poc/harness_isolated_get_named_data.cpp` — the isolated harness proving this bug independent of `get_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.