tsk-arasu's picture
Upload folder using huggingface_hub
dc9f5c2 verified
|
Raw
History Blame Contribute Delete
7.83 kB
# Null Pointer Dereference in ExecuTorch MethodMeta::get_backend_name() via Missing BackendDelegate.id (.pte)
**Target:** ExecuTorch 1.3.1 (.pte, huntr Model File Vulnerability program)
**Severity:** Low-Medium (Denial of Service)
**CWE:** CWE-476 (NULL Pointer Dereference)
**Component:** `runtime/executor/method_meta.cpp`
**Authentication Required:** No β€” only requires a victim application to load an attacker-supplied `.pte` file and call a public API.
## Summary
`MethodMeta::get_backend_name()` is a public API for enumerating the backend names used by a loaded method (e.g. for logging or diagnostics). It dereferences `BackendDelegate.id` without checking it for null, even though `id` is an ordinary (non-required) field in the schema. A `.pte` file with a well-formed `delegates` array containing an entry whose `id` is omitted crashes any process that calls `get_backend_name()` for that entry's index.
This is a distinct bug from the previously identified `uses_backend()` null-derefs (same file, same underlying `BackendDelegate.id` field, but a different function and call site) β€” confirmed independently below using a harness that calls **only** `get_backend_name()`, with zero calls to any other `MethodMeta` method, ruling out any possibility this is merely an artifact of a different bug being triggered first.
## Vulnerability Details
`schema/program.fbs`:
```
table BackendDelegate {
id: string; // not required
processed: BackendDelegateDataReference;
compile_specs: [CompileSpec];
}
```
`runtime/executor/method_meta.cpp` (pristine 1.3.1 source, lines 424–433):
```cpp
Result<const char*> MethodMeta::get_backend_name(size_t index) const {
const auto count = num_backends();
ET_CHECK_OR_RETURN_ERROR(
index < count,
InvalidArgument,
"Index %zu out of range. num_backends: %zu",
index,
count);
return s_plan_->delegates()->Get(index)->id()->c_str(); // <-- crash site, line 432
}
```
The bounds check (`index < count`) only validates the *index*, not the *content* of the `BackendDelegate` at that index. A `BackendDelegate` entry with no `id` is fully legal per the schema, so `id()` returns `nullptr` and `->c_str()` crashes.
## 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/03 Step 1 β€” the default target set includes `runtime/executor/method_meta.cpp`.
### 2. Build the isolated PoC harness (`poc/harness_isolated_get_backend_name.cpp`, included in this report)
This harness deliberately calls **only** `get_backend_name()` β€” no `uses_backend()`, no other `MethodMeta` accessor β€” to prove this bug is independent of any other finding in this codebase:
```cpp
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size) {
// ... load Program under InternalConsistency verification ...
auto meta = p.method_meta(name.get());
if (meta.ok()) {
auto& m = meta.get();
size_t nb = m.num_backends();
for (size_t bi = 0; bi < nb; ++bi) {
(void)m.get_backend_name(bi); // ONLY this call
}
}
}
```
```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_backend_name.cpp -o harness.o
clang++-16 -fsanitize=fuzzer,address,undefined -o poc_harness harness.o \
"$ET_BUILD/extension/data_loader/libextension_data_loader.a" \
"$ET_BUILD/libexecutorch_core.a"
```
### 3. PoC file
`poc/poc_get_backend_name_null.pte` (728 bytes, **included in this report β€” sha256 `7675e37734409f4ec13bc31bd21963b085312f65c33dfc583cd0d1521c22be0a`**) is a well-formed `.pte` file containing an `ExecutionPlan` whose `delegates` array has at least one `BackendDelegate` entry with `id` 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_backend_name_null.pte
```
### Expected result (secure behavior)
`get_backend_name()` should return a clean `Error` (e.g. `InvalidProgram`) for a delegate entry with no `id`, since there is no valid name to return.
### Actual result β€” verified in isolation (harness calls ONLY get_backend_name, no other MethodMeta method)
```
Running: poc/poc_get_backend_name_null.pte
runtime/executor/method_meta.cpp:432:50: runtime error: member call on null pointer of type 'flatbuffers::String'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior runtime/executor/method_meta.cpp:432:50 in
==<pid>== ERROR: libFuzzer: deadly signal
```
**Reproduced 3/3 identical runs, in isolation** (re-verified live for this report using a harness that calls no other `MethodMeta` method):
```
run 1: runtime/executor/method_meta.cpp:432:50: runtime error: member call on null pointer of type 'flatbuffers::String'
run 2: (identical)
run 3: (identical)
```
This isolation test is the key piece of evidence distinguishing this from a previously-identified `uses_backend()` bug in the same file: it proves `get_backend_name()` crashes on this exact field independent of any other code path being exercised first.
## Impact
**Who is affected:** Any application calling `MethodMeta::get_backend_name()` to enumerate backend names β€” a normal diagnostic/logging pattern for applications that report which backends a loaded model uses.
**What the attacker can do:** Cause a deterministic crash by supplying a `.pte` file with a `BackendDelegate` entry that has no `id` β€” legal per schema, undetected by both of ExecuTorch's verification levels.
**What's at risk:** Availability only.
**Why not Critical:** Controlled null-pointer dereference, no memory corruption or code execution.
## Suggested Remediation
```cpp
Result<const char*> MethodMeta::get_backend_name(size_t index) const {
const auto count = num_backends();
ET_CHECK_OR_RETURN_ERROR(
index < count,
InvalidArgument,
"Index %zu out of range. num_backends: %zu",
index,
count);
auto delegate = s_plan_->delegates()->Get(index);
ET_CHECK_OR_RETURN_ERROR(
delegate != nullptr && delegate->id() != nullptr,
InvalidProgram,
"Backend delegate %zu or its id is null",
index);
return delegate->id()->c_str();
}
```
**Design recommendation:** this is the second function in `method_meta.cpp` found to independently dereference `BackendDelegate.id` without a null check (the other being `uses_backend()`). A single shared helper β€” e.g. `Result<const char*> get_delegate_id(size_t index)` β€” used by both functions would prevent a third such call site from repeating this pattern in the future.
A regression test should build an `ExecutionPlan.delegates` entry with `id` omitted and assert `get_backend_name()` returns a clean `Error` rather than crashing.
## Files Included in This Report
- `poc/poc_get_backend_name_null.pte` β€” the 728-byte PoC file (sha256 `7675e37734409f4ec13bc31bd21963b085312f65c33dfc583cd0d1521c22be0a`)
- `poc/harness_isolated_get_backend_name.cpp` β€” the isolated harness proving this bug independent of any other `MethodMeta` code path
## 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_backend_name_null.pte` is ready for that upload.