File size: 7,661 Bytes
c6c65e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
149
150
# Null Pointer Dereference in ExecuTorch FlatTensorDataMap::get_key() 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 a public enumeration API.

## Summary

`FlatTensorDataMap::get_key()` is a public API for enumerating the names of tensors/blobs stored in a `.ptd` file by index β€” the standard pattern for an application to discover what data a file contains before requesting it by name. It dereferences `NamedData.key` without checking it for null, even though `key` is an ordinary (non-required) field in the schema. A `.ptd` file with a `NamedData` entry present in the `named_data` vector (so it is counted by `get_num_keys()`) but whose own `key` field is omitted crashes any process that enumerates keys by index β€” a completely standard, expected usage pattern, not an edge case.

**Confirmed against the pristine, unmodified ExecuTorch 1.3.1 source.**

## Vulnerability Details

`extension/flat_tensor/serialize/flat_tensor.fbs`:

```
table NamedData {
  key: string;              // not required
  segment_index: uint32;
  tensor_layout: TensorLayout;
}
```

`extension/flat_tensor/flat_tensor_data_map.cpp` (pristine 1.3.1 source, lines 197–211):

```cpp
ET_NODISCARD Result<uint32_t> FlatTensorDataMap::get_num_keys() const {
  return flat_tensor_->named_data()->size();
}

ET_NODISCARD Result<const char*> FlatTensorDataMap::get_key(
    uint32_t index) const {
  uint32_t num_keys = get_num_keys().get();
  ET_CHECK_OR_RETURN_ERROR(
      index >= 0 && index < num_keys,
      InvalidArgument,
      "Index %u out of range of size %u",
      index,
      num_keys);
  return flat_tensor_->named_data()->Get(index)->key()->c_str();   // <-- crash site, line 201 (offset within function)
}
```

`FlatTensorDataMap::load()`'s validation only checks that the `named_data` **vector itself** is non-null at the top level β€” it never inspects the fields of individual `NamedData` elements. `get_named_data()` (the linear-search helper backing `get_tensor_layout()`/`get_data()`/`load_data_into()`) happens to implicitly avoid crashing on a null `key` in some cases due to how its comparison is structured, but `get_key()` is a **completely separate, simpler code path** that enumerates by index directly β€” it does not go through `get_named_data()` at all, so it inherits none of that incidental behavior.

## 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 (`EXECUTORCH_BUILD_EXTENSION_FLAT_TENSOR=ON`).

### 2. Build the harness (`poc/harness_flat_tensor_fuzzer.cpp`, included β€” identical to REPORT-01's harness, which already calls `get_key()` for every index up to `get_num_keys()`)

```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_flat_tensor_fuzzer.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_key_null.ptd` (272 bytes, **included in this report β€” sha256 `427bc13020be820b41aafec2c6e00de360fe97185d3879d5ec770594c21ac39`**) 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_key_null.ptd
```

### Expected result (secure behavior)
`get_key()` should return a clean `Error` (e.g. `InvalidExternalData`) for an entry with no `key`, rather than crashing.

### Actual result β€” verified against the pristine, unmodified ExecuTorch 1.3.1 source

```
Running: poc/poc_get_key_null.ptd
extension/flat_tensor/flat_tensor_data_map.cpp:201:57: runtime error: member call on null pointer of type 'flatbuffers::String'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior extension/flat_tensor/flat_tensor_data_map.cpp:201:57 in
==<pid>== ERROR: libFuzzer: deadly signal
```

**Reproduced 3/3 identical runs against the pristine source** (re-verified live for this report):
```
run 1: extension/flat_tensor/flat_tensor_data_map.cpp:201:57: runtime error: member call on null pointer of type 'flatbuffers::String'
run 2: (identical)
run 3: (identical)
```

## Impact

**Who is affected:** Any application enumerating tensor/blob names in a `.ptd` file via `get_num_keys()` + `get_key()` β€” the standard discovery pattern for this format.

**What the attacker can do:** Cause a deterministic crash simply by having one entry in the `named_data` vector with no `key` β€” the file still loads successfully (the vector itself is present), so the crash only surfaces during ordinary enumeration.

**What's at risk:** Availability only.

**Why not Critical:** Controlled null-pointer dereference, no memory corruption or code execution.

## Suggested Remediation

```cpp
ET_NODISCARD Result<const char*> FlatTensorDataMap::get_key(
    uint32_t index) const {
  uint32_t num_keys = get_num_keys().get();
  ET_CHECK_OR_RETURN_ERROR(
      index >= 0 && index < num_keys,
      InvalidArgument,
      "Index %u out of range of size %u",
      index,
      num_keys);
  auto* named_data = flat_tensor_->named_data()->Get(index);
  ET_CHECK_OR_RETURN_ERROR(
      named_data != nullptr && named_data->key() != nullptr,
      InvalidExternalData,
      "NamedData entry %u or its key is null",
      index);
  return named_data->key()->c_str();
}
```

**Design recommendation:** this is one of several findings in this file (see companion reports on `get_tensor_layout()`/`create_tensor_layout()`) where the file validates the top-level presence of `named_data`/`segments` vectors at `load()` time but never validates the fields within each element. A single validation pass over `named_data()` at load time β€” or a shared per-entry validator called from every accessor β€” would close this entire class of findings at once rather than requiring a separate patch per accessor as each is independently discovered.

A regression test should build a `.ptd` with a `NamedData` entry that has `key` omitted, asserting `get_key()` returns a clean `Error` rather than crashing.

## Files Included in This Report

- `poc/poc_get_key_null.ptd` β€” the 272-byte PoC file (sha256 `427bc13020be820b41aafec2c6e00de360fe97185d3879d5ec770594c21ac39`)
- `poc/harness_flat_tensor_fuzzer.cpp` β€” the harness used to trigger and reproduce the crash (same binary as REPORT-01/02/04, which all target `extension/flat_tensor/flat_tensor_data_map.cpp`)

## 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_key_null.ptd` is ready for that upload.