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):

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:

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
    }
  }
}
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

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

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.