Null Pointer Dereference in ExecuTorch create_tensor_layout() via Missing TensorLayout.sizes (.ptd)
Target: ExecuTorch 1.3.1 (.ptd, huntr Model File Vulnerability program)
Severity: Low-Medium (Denial of Service)
CWE: CWE-476 (NULL Pointer Dereference)
Component: extension/flat_tensor/flat_tensor_data_map.cpp
Authentication Required: No β only requires a victim application to load an attacker-supplied .ptd file and call a public API.
Summary
NamedData.tensor_layout (a TensorLayout table) can be present in a .ptd file while its own sizes field (and/or dim_order) is itself omitted β sizes: [int32] and dim_order: [uint8] are both ordinary, non-required fields in the schema. create_tensor_layout() dereferences tensor_layout->sizes()->size() without checking sizes() for null. This is a distinct, deeper defect than a simple "is tensor_layout itself present" check: a NamedData entry can have a fully non-null tensor_layout table and still crash the parser, because the crash is one level further down in the field hierarchy.
Confirmed against the pristine, unmodified ExecuTorch 1.3.1 source β this is a real defect in the shipped code, not an artifact of any local investigation tooling.
Vulnerability Details
extension/flat_tensor/serialize/flat_tensor.fbs:
table TensorLayout {
scalar_type: executorch_flatbuffer.ScalarType;
sizes: [int32]; // not required
dim_order: [uint8]; // not required
}
extension/flat_tensor/flat_tensor_data_map.cpp (pristine 1.3.1 source, lines 97β108):
Result<const TensorLayout> create_tensor_layout(
const flat_tensor_flatbuffer::TensorLayout* tensor_layout) {
ScalarType scalar_type =
static_cast<ScalarType>(tensor_layout->scalar_type());
const int dim = tensor_layout->sizes()->size(); // <-- crash site: sizes() may be null
const auto serialized_sizes = tensor_layout->sizes()->data();
const auto serialized_dim_order = tensor_layout->dim_order()->data();
return TensorLayout::create(
Span<const int32_t>(serialized_sizes, dim),
Span<const uint8_t>(serialized_dim_order, dim),
scalar_type);
}
Note: this is the unmodified upstream line numbering (line 101 in the actual shipped 1.3.1 source tree; some of this report's supporting artifacts were produced against a locally-patched copy for unrelated reasons β see the Verification section below for the pristine-source confirmation).
get_named_data()'s validation (segment_index bounds, segment offset/size overflow checks) never inspects the internal fields of tensor_layout β it only validates the NamedData entry's own segment_index. No code path β not get_named_data(), not create_tensor_layout(), not TensorLayout::create() in runtime/core/tensor_layout.cpp β checks whether tensor_layout->sizes() or tensor_layout->dim_order() are non-null before they are used.
Steps to Reproduce
Environment
Same sanitized build and harness as REPORT-01 (identical poc/harness_flat_tensor_fuzzer.cpp, identical build commands against the pristine, unmodified ExecuTorch 1.3.1 source tree).
1. PoC file
poc/poc_tensorlayout_sizes_null.ptd (272 bytes, included in this report β sha256 f9c20d5697f9d147938eb31b188a14f82767252af47f5075358b7bc98c802476) is a well-formed .ptd file with:
- One
NamedDataentry:key = "weight1",segment_index = 0 tensor_layoutpresent (non-null), withscalar_typeset to a valid valuetensor_layout.sizesomitted
This exact file was found via coverage-guided fuzzing and is included verbatim β it is the precise reproducer verified below, not a re-derived approximation.
Note on
poc/gen_poc.py: a deterministic regeneration script is included for transparency and to demonstrate the minimal JSON shape that triggers this bug, but flatc's table-encoding is not guaranteed to be byte-identical across independently-constructed JSON inputs that describe the same logical structure. The regenerated file triggers the identical crash (flat_tensor_data_map.cpp:101:43, sameruntime error: member call on null pointer of type 'flatbuffers::Vector<int>') but is not byte-for-byte identical to the included PoC. Use the includedpoc_tensorlayout_sizes_null.ptdfile directly for exact reproduction; usegen_poc.pyonly to understand or reconstruct the minimal triggering structure.
2. Build the harness and trigger the crash
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_flat_tensor_fuzzer.cpp -o harness.o
clang++-16 -fsanitize=fuzzer,address,undefined -o poc_harness harness.o \
"$ET_BUILD/extension/flat_tensor/libextension_flat_tensor.a" \
"$ET_BUILD/extension/data_loader/libextension_data_loader.a" \
"$ET_BUILD/libexecutorch_core.a"
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_tensorlayout_sizes_null.ptd
Expected result (secure behavior)
get_tensor_layout("weight1") should return a clean Error::InvalidExternalData, since a TensorLayout with no sizes cannot describe a valid tensor shape.
Actual result β verified against the pristine (unmodified) ExecuTorch 1.3.1 source
Running: poc/poc_tensorlayout_sizes_null.ptd
extension/flat_tensor/flat_tensor_data_map.cpp:101:43: runtime error: member call on null pointer of type 'flatbuffers::Vector<int>'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior extension/flat_tensor/flat_tensor_data_map.cpp:101:43 in
==<pid>== ERROR: libFuzzer: deadly signal
Reproduced 3/3 identical runs against the pristine, unmodified source tree (re-verified live for this report, not against any locally-modified copy):
run 1: extension/flat_tensor/flat_tensor_data_map.cpp:101:43: runtime error: member call on null pointer of type 'flatbuffers::Vector<int>'
run 2: (identical)
run 3: (identical)
Impact
Who is affected: Any application calling FlatTensorDataMap::get_tensor_layout() or load_data_into() on a NamedData entry whose tensor_layout is present but incomplete.
What the attacker can do: Cause a deterministic crash with a .ptd file that appears more "valid" than the minimal ET-NEW-001 case (it has a non-null tensor_layout table with a set scalar_type) β meaning a shallow "is tensor_layout null" check alone is insufficient defense; the internal fields of that table must also be validated.
What's at risk: Availability only. No memory corruption or code execution.
Why not Critical: Same reasoning as the other null-pointer-dereference findings in this codebase β a controlled crash, not a memory-safety violation.
Suggested Remediation
In create_tensor_layout(), after checking tensor_layout itself for null, additionally check its sizes and dim_order fields:
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 (tensor_layout->sizes() == nullptr || tensor_layout->dim_order() == nullptr) {
return Error::InvalidExternalData;
}
const int dim = tensor_layout->sizes()->size();
const auto serialized_sizes = tensor_layout->sizes()->data();
const auto serialized_dim_order = tensor_layout->dim_order()->data();
return TensorLayout::create(
Span<const int32_t>(serialized_sizes, dim),
Span<const uint8_t>(serialized_dim_order, dim),
scalar_type);
}
A regression test should build a .ptd NamedData.tensor_layout with sizes/dim_order omitted and assert a clean Error return.
Files Included in This Report
poc/poc_tensorlayout_sizes_null.ptdβ the exact 272-byte PoC file used for all verification in this report (sha256f9c20d5697f9d147938eb31b188a14f82767252af47f5075358b7bc98c802476)poc/gen_poc.pyβ illustrative regeneration script (produces an equivalent-but-not-byte-identical file that triggers the same crash line β see note above)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_tensorlayout_sizes_null.ptd is ready for that upload.