tsk-arasu's picture
Upload folder using huggingface_hub
9de4301 verified
|
Raw
History Blame Contribute Delete
7.98 kB

Reachable Assertion Abort in ExecuTorch create_tensor_layout() via Unvalidated ScalarType (.ptd)

Target: ExecuTorch 1.3.1 (.ptd, huntr Model File Vulnerability program) Severity: Low-Medium (Denial of Service) CWE: CWE-20 (Improper Input Validation) Component: extension/flat_tensor/flat_tensor_data_map.cpp (call site) / runtime/core/exec_aten/util/scalar_type_util.h (abort site) Authentication Required: No β€” only requires a victim application to load an attacker-supplied .ptd file and call a public API.

Summary

create_tensor_layout() casts the raw scalar_type byte from a .ptd's TensorLayout table directly to the ScalarType enum with no validation, then passes it into TensorLayout::create(), which eventually calls elementSize() to compute the tensor's byte size. elementSize()'s switch statement has no default case that returns an error β€” it calls ET_CHECK_MSG(false, "Unknown ScalarType..."), which aborts the process. An out-of-range scalar_type byte value in an otherwise well-formed .ptd file therefore causes a deliberate but attacker-reachable assertion abort.

Notably, the equivalent .pte code path (tensor_parser_exec_aten.cpp's parseTensor()) does validate scalar_type with isValid() before use and returns a clean InvalidProgram error for exactly this case β€” proving this .ptd code path is missing a check that the parallel .pte code path already implements correctly.

Confirmed against the pristine, unmodified ExecuTorch 1.3.1 source.

Vulnerability Details

runtime/executor/tensor_parser_exec_aten.cpp (the correct, existing pattern for .pte tensors):

ScalarType scalar_type = static_cast<ScalarType>(s_tensor->scalar_type());
ET_CHECK_OR_RETURN_ERROR(
    isValid(scalar_type),
    InvalidProgram,
    "Invalid or unsupported ScalarType %" PRId8,
    static_cast<int8_t>(scalar_type));

extension/flat_tensor/flat_tensor_data_map.cpp's create_tensor_layout() (the .ptd TensorLayout equivalent β€” pristine 1.3.1 source, lines 97–108) has no such check:

Result<const TensorLayout> create_tensor_layout(
    const flat_tensor_flatbuffer::TensorLayout* tensor_layout) {
  ScalarType scalar_type =
      static_cast<ScalarType>(tensor_layout->scalar_type());   // no isValid() check
  const int dim = tensor_layout->sizes()->size();
  ...
  return TensorLayout::create(
      Span<const int32_t>(serialized_sizes, dim),
      Span<const uint8_t>(serialized_dim_order, dim),
      scalar_type);
}

runtime/core/exec_aten/util/scalar_type_util.h's elementSize():

inline size_t elementSize(::executorch::aten::ScalarType t) {
#define CASE_ELEMENTSIZE_CASE(ctype, name) \
  case ::executorch::aten::ScalarType::name: return sizeof(ctype);

  switch (t) {
    ET_FORALL_SCALAR_TYPES(CASE_ELEMENTSIZE_CASE)
    default:
      ET_CHECK_MSG(false, "Unknown ScalarType %" PRId8, static_cast<int8_t>(t));  // <-- aborts
  }
#undef CASE_ELEMENTSIZE_CASE
}

ET_CHECK_MSG(false, ...) in this codebase calls runtime_abort() β†’ pal_abort() β†’ et_pal_abort(), terminating the process. This macro is designed for invariants the code asserts should never occur given prior validation β€” it is not designed to directly validate untrusted external input. Because create_tensor_layout() never validates scalar_type before it flows into this call chain, an attacker can trigger this "should never happen" path on demand.

Steps to Reproduce

Environment

Linux x86-64, ExecuTorch 1.3.1 pristine source, clang-16, CMake, Ninja. No authentication, no host access.

1. Build and harness

Identical to REPORT-01 (same build flags, same poc/harness_flat_tensor_fuzzer.cpp).

2. PoC file

poc/poc_invalid_scalar_type.ptd (272 bytes, included in this report β€” sha256 b5972dad3d9e5cf23434270c1dadc2a03dd51b3c4fde8661b38f1e8a1e9494fd) is a well-formed .ptd file with one NamedData entry (key = "weight1") whose tensor_layout has valid sizes/dim_order but a scalar_type byte value outside the range defined by ET_FORALL_SCALAR_TYPES.

3. 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_invalid_scalar_type.ptd

Expected result (secure behavior)

get_tensor_layout("weight1") should return a clean Error::InvalidExternalData for an out-of-range scalar_type, matching the behavior of the equivalent .pte Tensor parsing path.

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

Full symbolized stack trace (via addr2line against the built binary):

et_pal_abort
    runtime/platform/default/posix.cpp:98
executorch::runtime::pal_abort()
    runtime/platform/platform.cpp:126
executorch::runtime::runtime_abort()
    runtime/platform/abort.cpp:20
executorch::runtime::TensorLayout::create(...)
    runtime/core/exec_aten/util/scalar_type_util.h:419   <- elementSize()'s ET_CHECK_MSG
executorch::extension::(anonymous namespace)::create_tensor_layout(...)
    extension/flat_tensor/flat_tensor_data_map.cpp:104
executorch::extension::FlatTensorDataMap::get_tensor_layout(...)
    extension/flat_tensor/flat_tensor_data_map.cpp:122
LLVMFuzzerTestOneInput
==<pid>== ERROR: libFuzzer: deadly signal
    #0 ... (abort machinery)
    ...
NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal

Reproduced 3/3 identical runs against the pristine source (re-verified live for this report, with full symbol resolution confirming the exact call chain above).

Impact

Who is affected: Any application calling get_tensor_layout()/load_data_into() on a NamedData entry whose tensor_layout.scalar_type is out of range.

What the attacker can do: Cause a deterministic crash β€” and notably, unlike the UBSan-trapped null-dereference findings in this codebase, this is a deliberate abort() call that fires identically in production (non-sanitized) builds, not merely undefined behavior that happens to crash under a sanitizer. This makes it a slightly stronger, more production-relevant DoS guarantee than the other findings in this batch.

What's at risk: Availability only. No memory corruption, no code execution β€” ET_CHECK_MSG performs a controlled, deliberate process termination.

Why not Critical: A deliberate abort(), not memory corruption or code execution.

Suggested Remediation

Result<const TensorLayout> create_tensor_layout(
    const flat_tensor_flatbuffer::TensorLayout* tensor_layout) {
  if (tensor_layout == nullptr) {
    return Error::InvalidExternalData;
  }
  ScalarType scalar_type = static_cast<ScalarType>(tensor_layout->scalar_type());
  if (!executorch::runtime::isValid(scalar_type)) {
    return Error::InvalidExternalData;
  }
  const int dim = tensor_layout->sizes()->size();
  ...

This is a direct application of the pattern already correctly implemented in tensor_parser_exec_aten.cpp's parseTensor() for the parallel .pte Tensor type β€” no new design is needed, just consistency between the two code paths.

A regression test should build a .ptd NamedData.tensor_layout with an out-of-range scalar_type byte and assert get_tensor_layout() returns a clean Error rather than aborting.

Files Included in This Report

  • poc/poc_invalid_scalar_type.ptd β€” the 272-byte PoC file (sha256 b5972dad3d9e5cf23434270c1dadc2a03dd51b3c4fde8661b38f1e8a1e9494fd)
  • poc/harness_flat_tensor_fuzzer.cpp β€” the harness used to trigger and reproduce the crash

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_invalid_scalar_type.ptd is ready for that upload.