1515#include < executorch/runtime/executor/method.h>
1616#include < executorch/runtime/executor/program.h>
1717#include < executorch/runtime/executor/test/managed_memory_manager.h>
18+ #include < executorch/runtime/kernel/kernel_runtime_context.h>
1819#include < executorch/runtime/kernel/operator_registry.h>
1920#include < executorch/runtime/platform/runtime.h>
2021#include < executorch/schema/program_generated.h>
@@ -78,7 +79,8 @@ std::vector<uint8_t> CreateTestProgram(
7879 const std::vector<int32_t >& input_indices = {},
7980 // If true, emit a chain of two KernelCalls: the first's op_index is out of
8081 // range so resolve_operator() fails on a non-final instruction.
81- bool with_out_of_range_kernel_call = false ) {
82+ bool with_out_of_range_kernel_call = false ,
83+ bool with_failing_kernel_no_overload = false ) {
8284 flatbuffers::FlatBufferBuilder builder (1024 );
8385
8486 std::vector<flatbuffers::Offset<executorch_flatbuffer::EValue>> evalues;
@@ -161,12 +163,33 @@ std::vector<uint8_t> CreateTestProgram(
161163 /* outputs=*/ builder.CreateVector (std::vector<int32_t >{}),
162164 builder.CreateVector (instructions),
163165 /* stacktrace=*/ 0 ));
166+ } else if (with_failing_kernel_no_overload) {
167+ auto kernel_call = executorch_flatbuffer::CreateKernelCall (
168+ builder, /* op_index=*/ 0 , builder.CreateVector (std::vector<int32_t >{}));
169+ std::vector<flatbuffers::Offset<executorch_flatbuffer::Instruction>>
170+ instructions{executorch_flatbuffer::CreateInstruction (
171+ builder,
172+ executorch_flatbuffer::InstructionArguments::KernelCall,
173+ kernel_call.Union ())};
174+ chains.push_back (
175+ executorch_flatbuffer::CreateChain (
176+ builder,
177+ /* inputs=*/ builder.CreateVector (std::vector<int32_t >{}),
178+ /* outputs=*/ builder.CreateVector (std::vector<int32_t >{}),
179+ builder.CreateVector (instructions),
180+ /* stacktrace=*/ 0 ));
164181 }
165182 auto chain_vec = builder.CreateVector (chains);
166183 std::vector<flatbuffers::Offset<executorch_flatbuffer::Operator>> operators;
167184 if (with_out_of_range_kernel_call) {
168185 operators.push_back (executorch_flatbuffer::CreateOperator (
169186 builder, builder.CreateString (" test::noop" ), builder.CreateString (" " )));
187+ } else if (with_failing_kernel_no_overload) {
188+ operators.push_back (
189+ executorch_flatbuffer::CreateOperator (
190+ builder,
191+ builder.CreateString (" test::failing_no_overload" ),
192+ /* overload=*/ 0 ));
170193 }
171194 auto operators_vec = builder.CreateVector (operators);
172195 auto empty_delegates_vec = builder.CreateVector (
@@ -189,14 +212,19 @@ std::vector<uint8_t> CreateTestProgram(
189212 plans.push_back (execution_plan);
190213 auto plans_vec = builder.CreateVector (plans);
191214
215+ auto constant_segment = executorch_flatbuffer::CreateSubsegmentOffsets (
216+ builder,
217+ /* segment_index=*/ 0 ,
218+ builder.CreateVector (std::vector<uint64_t >{0 }));
219+
192220 auto program = executorch_flatbuffer::CreateProgram (
193221 builder,
194222 /* version=*/ 0 ,
195223 plans_vec,
196224 /* constant_buffer=*/ 0 ,
197225 /* backend_delegate_data=*/ 0 ,
198226 /* segments=*/ 0 ,
199- /* constant_segment= */ 0 ,
227+ constant_segment,
200228 /* mutable_data_segments=*/ 0 ,
201229 /* named_data=*/ 0 );
202230
@@ -404,3 +432,36 @@ TEST_F(ProgramValidationTest, ResolveOperatorFailureSurfacesFromInit) {
404432 program->load_method (" forward" , &mmm.get ());
405433 EXPECT_EQ (method.error (), Error::InvalidProgram);
406434}
435+
436+ TEST_F (ProgramValidationTest, KernelCallFailureWithNullOverloadDoesNotCrash) {
437+ static const auto fail_kernel = executorch::runtime::Kernel (
438+ " test::failing_no_overload" ,
439+ [](executorch::runtime::KernelRuntimeContext& context,
440+ executorch::runtime::Span<executorch::runtime::EValue*>) {
441+ context.fail (Error::Internal);
442+ });
443+ ASSERT_EQ (executorch::runtime::register_kernel (fail_kernel), Error::Ok);
444+
445+ AlignedBuffer buf (CreateTestProgram (
446+ /* configs=*/ {},
447+ /* input_indices=*/ {},
448+ /* with_out_of_range_kernel_call=*/ false ,
449+ /* with_failing_kernel_no_overload=*/ true ));
450+ auto loader = buf.loader ();
451+
452+ Result<Program> program =
453+ Program::load (&loader, Program::Verification::InternalConsistency);
454+ ASSERT_EQ (program.error (), Error::Ok);
455+
456+ executorch::runtime::testing::ManagedMemoryManager mmm (
457+ /* planned_memory_bytes=*/ 32 * 1024U ,
458+ /* method_allocator_bytes=*/ 32 * 1024U );
459+ Result<executorch::runtime::Method> method =
460+ program->load_method (" forward" , &mmm.get ());
461+ ASSERT_EQ (method.error (), Error::Ok);
462+
463+ // KernelCall failure logs operator overload. With a null overload in the
464+ // flatbuffer operator table, this previously segfaulted (#23955).
465+ Error err = method->execute ();
466+ EXPECT_EQ (err, Error::Internal);
467+ }
0 commit comments