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):
```cpp
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:
```cpp
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()`:
```cpp
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
```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_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
```cpp
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.