tsk-arasu's picture
Upload folder using huggingface_hub
1ec1150 verified
|
Raw
History Blame Contribute Delete
9.11 kB
# Null Pointer Dereference in ExecuTorch Program::get_backend_delegate_data() via Missing Program.backend_delegate_data (.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/program.cpp`
**Authentication Required:** No β€” requires a victim application to load a `.pte` file whose method has a backend delegate referencing INLINE-located processed data, with at least one backend registered/available (a routine deployment configuration).
## Summary
`Program::get_backend_delegate_data()` dereferences `Program.backend_delegate_data()` (an optional top-level flatbuffer field) directly inside its bounds-check condition, with no null guard. A `.pte` file with an `ExecutionPlan.delegates` entry whose `processed` field specifies `DataLocation::INLINE`, while the `Program`'s top-level `backend_delegate_data` list is entirely absent, crashes the moment `Method::init()` attempts to resolve that delegate β€” before any execution begins. Unlike a sibling finding in this same investigation (the `Operator.overload` null-deref), this crash sits directly in the bounds-check condition itself, not merely inside a log-message argument β€” making it **unconditionally reachable regardless of build configuration** (no dependency on `EXECUTORCH_ENABLE_LOGGING`).
This bug was confirmed **twice independently**: once against this session's incrementally patched build, and once against a **freshly rebuilt, wholly unmodified pristine 1.3.1 source tree**, ruling out any possibility of a local-patch artifact.
## Vulnerability Details
`runtime/executor/program.cpp`'s `Program::get_backend_delegate_data()`:
```cpp
Error Program::get_backend_delegate_data(
size_t index,
const void** out_data,
size_t* out_size) const {
const auto* data_list =
static_cast<const executorch_flatbuffer::Program*>(internal_program_)
->backend_delegate_data();
ET_CHECK_OR_RETURN_ERROR(
index < data_list->size(), // <-- crash site: data_list may be null
NotFound,
"index %zu >= list size %" PRIu32,
index,
data_list->size());
...
```
`Program.backend_delegate_data` is declared as an optional `[BackendDelegateInlineData]` list in `schema/program.fbs`:
```
// List of delegate data. Pointed to by BackendDelegateDataReference.
backend_delegate_data: [BackendDelegateInlineData];
```
This function is called from `BackendDelegate::GetProcessedData()` (`method.cpp`) whenever a delegate's `processed` field specifies `DataLocation::INLINE`:
```cpp
case executorch_flatbuffer::DataLocation::INLINE: {
const void* data;
size_t size;
Error err = program->get_backend_delegate_data(
processed->index(), &data, &size);
...
```
Which in turn is called from `BackendDelegate::Init()` during `Method::init()`'s delegate-resolution loop β€” reached during ordinary method **loading**, for any delegate whose registered backend is available (`get_backend_class(id) != nullptr && backend->is_available()`), a standard, expected condition for any deployment that has compiled in and registered at least one hardware-acceleration backend (which most real ExecuTorch deployments do).
## 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 (logging enabled or disabled β€” this bug reproduces either way)
Same build as REPORT-11 Step 1 (or the simpler REPORT-01 build β€” `EXECUTORCH_ENABLE_LOGGING` does not affect this finding).
### 2. Build the execute-level harness (`poc/harness_execute_fuzzer.cpp`, same harness as REPORT-11, included in this report)
This harness registers fake backends under common names (`XnnpackBackend`, `QnnBackend`, `CoreMLBackend`, etc.) that trivially succeed on `init()`, letting us exercise the real `BackendDelegate::Init()` code path:
```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_execute_fuzzer.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_backend_delegate_data_null.pte` (304 bytes, **included in this report β€” sha256 `2f3be1201b1a426b19d44d2b0886ba12ed872ec4e3f4b02d65457b6c4f441165`**) contains:
- One `BackendDelegate` entry with `id = "XnnpackBackend"` and `processed = { location: INLINE, index: 0 }`
- **No** `backend_delegate_data` field at the `Program` level at all
### 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_backend_delegate_data_null.pte
```
### Expected result (secure behavior)
`Method::init()` should return `Error::NotFound` (or similar) cleanly when a delegate references INLINE data but the Program has no `backend_delegate_data` list, rather than crashing while checking bounds.
### Actual result β€” verified against BOTH a patched build AND an independently rebuilt, wholly unmodified pristine ExecuTorch 1.3.1 source tree
```
Running: poc/poc_backend_delegate_data_null.pte
runtime/executor/program.cpp:541:3: runtime error: member call on null pointer of type 'flatbuffers::Vector<flatbuffers::Offset<executorch_flatbuffer::BackendDelegateInlineData>>'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior runtime/executor/program.cpp:541:3 in
==<pid>== ERROR: libFuzzer: deadly signal
#0 ... (abort machinery)
Program::get_backend_delegate_data (program.cpp:541)
BackendDelegate::GetProcessedData (method.cpp:202)
BackendDelegate::Init (method.cpp:97)
Method::init (method.cpp:971)
Method::load (method.cpp:862)
```
**Reproduced 3/3 identical runs against the independently rebuilt pristine source** (re-verified live for this report, using a fresh build with zero local modifications):
```
run 1: member call on null pointer of type 'flatbuffers::Vector<...>'
run 2: (identical)
run 3: (identical)
```
## Impact
**Who is affected:** Any application calling `Program::load_method()` on a method whose delegates reference INLINE processed data, where the Program's `backend_delegate_data` list is absent β€” this is triggered during **loading**, not execution, and requires only that the victim has registered a backend under a name the attacker can predict (most deployments register at least one real hardware-acceleration backend).
**What the attacker can do:** Cause a deterministic crash during method loading, unconditionally (no build-flag dependency).
**What's at risk:** Availability only.
**Why not Critical:** Controlled null-pointer dereference, no memory corruption or code execution demonstrated.
## Suggested Remediation
```cpp
Error Program::get_backend_delegate_data(
size_t index,
const void** out_data,
size_t* out_size) const {
const auto* data_list =
static_cast<const executorch_flatbuffer::Program*>(internal_program_)
->backend_delegate_data();
if (data_list == nullptr) {
ET_LOG(Error, "Program has no backend_delegate_data list");
return Error::NotFound;
}
ET_CHECK_OR_RETURN_ERROR(
index < data_list->size(),
NotFound,
"index %zu >= list size %" PRIu32,
index,
data_list->size());
...
```
**Design recommendation:** consider validating at `Program::load()` time (in `validate_program()`, `runtime/executor/program_validation.cpp`) that if any `ExecutionPlan.delegates[].processed.location == INLINE` exists anywhere in the program, `Program.backend_delegate_data` must also be present β€” catching this cross-field consistency issue once, at load time, rather than at each individual accessor.
A regression test should build a `.pte` with one `BackendDelegate` referencing INLINE processed data at index 0, and no `backend_delegate_data` field on the `Program`, asserting `Method::load()` returns a clean `Error::NotFound` rather than crashing.
## Files Included in This Report
- `poc/poc_backend_delegate_data_null.pte` β€” the 304-byte PoC file (sha256 `2f3be1201b1a426b19d44d2b0886ba12ed872ec4e3f4b02d65457b6c4f441165`)
- `poc/harness_execute_fuzzer.cpp` β€” the harness used to trigger and reproduce the crash, including fake backend registration for exercising `BackendDelegate::Init()`
## 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_backend_delegate_data_null.pte` is ready for that upload.