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 (sha256b5972dad3d9e5cf23434270c1dadc2a03dd51b3c4fde8661b38f1e8a1e9494fd)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.