File size: 7,980 Bytes
9de4301 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | # 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.
|