File size: 8,565 Bytes
9b78f96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 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.