Skip to content

executor_runner segfaults in portable kernels when a .pte tensor has neither a data buffer nor a memory plan (null data pointer) #23955

Description

@professor-moody

🐛 Describe the bug

A .pte can declare a tensor with data_buffer_idx == 0 and no allocation_info. getTensorDataPtr (runtime/executor/tensor_parser_exec_aten.cpp:289-291 at main 897d28fe) accepts this case as "pointer received at runtime" and returns nullptr. If that tensor is not a plan input, nothing requires a data pointer to be supplied before Method::execute(): execute checks only input_set_ (method.cpp:1792-1799). The portable kernels then load or store through the null pointer (kernels/portable/cpu/util/dtype_util.h:20-27, elementwise_util.h:191), because Tensor::const_data_ptr / mutable_data_ptr (runtime/core/portable_type/tensor.h:135-153) do not check for null.

Reproduced on 2026-10-18 at main 897d28fe3094f27898115ebd65fecafdef5970cb (Linux x86_64, clang 21.1.8) with the shipped examples/portable/executor_runner --model_path <file>.pte. Seven fuzzer-generated .pte files (704 to 764 bytes each; all seven fault inside add_out) were run through three builds:

                     ASan+UBSan -O1   Release -O3 -DNDEBUG   Release + logging
7 crafted .pte       exit 1           exit 139 (SIGSEGV)     exit 139
upstream add model   exit 0           exit 0                 exit 0

UBSan reports, for example:

kernels/portable/cpu/util/elementwise_util.h:191:11: runtime error: store to null pointer of type 'float'
kernels/portable/cpu/util/dtype_util.h:21:26: runtime error: load of null pointer of type 'const float'
  ... torch::executor::native::add_out(...)
  ... executorch::runtime::Method::execute_instruction() runtime/executor/method.cpp:1557:7
  ... executorch::runtime::Method::execute() runtime/executor/method.cpp:1844:21
  ... main examples/portable/executor_runner/executor_runner.cpp:856:28

I found #19269, which proposed a null check in the six Tensor data accessors to cover a large class of internal fuzzer crashes. It was closed without merging, and the accessors at 897d28fe still have no check. This report adds a file-only path to the same state: the .pte alone produces the null-data tensor, through the stock runner's default flow. Possible fixes are to reject such a tensor at load unless it is a plan input or output, to require output pointers to be set before execute(), or to add the accessor check from #19269. I am happy to send a PR for whichever you prefer.

Two smaller null dereferences on the same malformed-input path, also reproduced at 897d28fe with the shipped runner:

  • Method::execute_instruction formats op->overload()->c_str() in the "KernelCall failed" ET_LOG (method.cpp:1565-1573) without the has_overload guard that populate_operator_name uses at load time (:756-768). A .pte whose operator has no overload string, plus any kernel check failure, segfaults in an -O3 -DNDEBUG runner built with EXECUTORCH_ENABLE_LOGGING=ON (exit 139; ASan: SEGV at address 0x4 in strlen via vsnprintf from method.cpp:1565). The same .pte without the kernel failure runs normally, and a kernel failure with the overload present exits cleanly with the logged error.
  • get_named_data (extension/flat_tensor/flat_tensor_data_map.cpp:66) and FlatTensorDataMap::get_key (:263) call key()->size() / key()->c_str() without a null check. A .ptd with a NamedData entry that omits key segfaults at address 0 in executor_runner --model_path ... --data_path ... (ASan: flat_tensor_data_map.cpp:66:50: runtime error: member call on null pointer of type 'flatbuffers::Vector<char>').

I have a small patch for these two, returning InvalidExternalData for a missing key and using "" for a missing overload. I can attach the crafted files, the controls, and the generator for the last two on request. Every run above recorded the SHA-256 of the binary and the input before and after.

Versions

main 897d28fe3094f27898115ebd65fecafdef5970cb (2026-10-18), built from source; Fedora 43 x86_64, clang 21.1.8, cmake 3.31.11. Release builds use -DCMAKE_BUILD_TYPE=Release (-O3 -DNDEBUG). Release v1.5.1 has the same source at these sites but was not run. (The template's collect_env.py output is not applicable to a C++ source build; paste it if the maintainers ask.)

Disclosure

AI tooling was used and assisted in the discovery and authoring of this issue.

cc @larryliu0820 @JacobSzwejbka @lucylq

Activity

  1. added theissue type on Oct 23, 2026
  2. JacobSzwejbka commented on Oct 28, 2026

    @JacobSzwejbka
    Contributor

    I can look into this eventually but if someone wants to put up a patch I can review it

  3. moved this from To triage to Backlog in ExecuTorch Coreon Oct 28, 2026
  4. added
    triagedThis issue has been looked at a team member, and triaged and prioritized into an appropriate module
    on Oct 28, 2026
  5. nil-is-all commented on Nov 10, 2026

    @nil-is-all
    Contributor

    @professor-moody could you also take a look at #24801?

  6. professor-moody commented on Nov 10, 2026

    @professor-moody
    Author

    Thanks for the ping, @nil-is-all.

    I reviewed #24801:

    1. Issue linkage (Part of #23955, not Fixes #23955): [runtime] Fix null operator overload logging and named data key dereferences #24801 covers the two secondary null dereferences at the bottom of executor_runner segfaults in portable kernels when a .pte tensor has neither a data buffer nor a memory plan (null data pointer) #23955 (op->overload() in the KernelCall failure log and NamedData::key() in FlatTensorDataMap), not the primary .pte null-tensor-data execution crash. Please switch Fixes #23955 to Part of #23955 so merging [runtime] Fix null operator overload logging and named data key dereferences #24801 does not close executor_runner segfaults in portable kernels when a .pte tensor has neither a data buffer nor a memory plan (null data pointer) #23955 early.
    2. Release -Werror=unused-variable break (runtime/executor/method.cpp): When ET_LOG_ENABLED=0 (default in Release), ET_LOG expands to ((void)0), leaving op_name and op_overload unused. Inlining the op->overload() check directly inside the ET_LOG arguments (matching populate_operator_name) avoids the unused locals and drops the unreachable op == nullptr / op->name() == nullptr branches.
    3. Fail closed on keyless .ptd entries (extension/flat_tensor/flat_tensor_data_map.cpp): get_named_data currently continues past entry->key() == nullptr, which turns a malformed .ptd into Error::NotFound in get_tensor_layout, get_data, and load_data_into while get_key returns InvalidExternalData. get_named_data should also return Error::InvalidExternalData.
    4. Regression tests: The added UINT32_MAX assertion already passed before [runtime] Fix null operator overload logging and named data key dereferences #24801; it needs a FlatBufferBuilder .ptd test with an omitted NamedData::key (exercising both get_key and get_data/get_tensor_layout) and an executor test where a KernelCall fails on an executorch_flatbuffer::Operator with an omitted overload.

    Separately, I opened #24870 (Part of #23955, disjoint hunks) to validate non-empty null-data tensors during Method execution for the primary crash in #23955.

  7. added a commit that references this issue on Nov 11, 2026
    f4cb9a8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

buggood first issueGood for newcomersmodule: runtimeIssues related to the core runtime and code under runtime/triagedThis issue has been looked at a team member, and triaged and prioritized into an appropriate module

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions