Skip to content

Commit 45d9231

Browse files
authored
Fix null pointer dereference in get_output_flattening_encoding (#16704)
Fix null pointer dereference vulnerability in Program::get_output_flattening_encoding() that could crash when processing malformed PTE files. The function dereferenced container_meta_type and encoded_out_str without null checks. Since these fields are optional in the FlatBuffer schema (not marked as required), a malformed PTE file with missing fields would cause a segfault, enabling denial-of-service attacks. Changes: - Add null checks for container_meta_type and encoded_out_str, returning Error::InvalidProgram with descriptive error messages instead of crashing - Add regression tests that verify malformed PTE files return errors - Enable program_test target in CMakeLists.txt
1 parent 65bb3b0 commit 45d9231

2 files changed

Lines changed: 173 additions & 1 deletion

File tree

runtime/executor/program.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,17 @@ Result<const char*> Program::get_output_flattening_encoding(
425425
if (!plan.ok()) {
426426
return plan.error();
427427
}
428-
return plan.get()->container_meta_type()->encoded_out_str()->c_str();
428+
auto* container_meta_type = plan.get()->container_meta_type();
429+
ET_CHECK_OR_RETURN_ERROR(
430+
container_meta_type != nullptr,
431+
InvalidProgram,
432+
"Missing container_meta_type in execution plan");
433+
auto* encoded_out_str = container_meta_type->encoded_out_str();
434+
ET_CHECK_OR_RETURN_ERROR(
435+
encoded_out_str != nullptr,
436+
InvalidProgram,
437+
"Missing encoded_out_str in container_meta_type");
438+
return encoded_out_str->c_str();
429439
}
430440

431441
Error Program::get_backend_delegate_data(

runtime/executor/test/program_test.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,165 @@ TEST_F(ProgramTest, LoadAndCheckPTESize) {
618618
Result<Program> truncated_program = Program::load(&truncated_loader);
619619
ASSERT_EQ(truncated_program.error(), Error::InvalidProgram);
620620
}
621+
622+
// Regression test for null pointer dereference in
623+
// get_output_flattening_encoding when container_meta_type is missing from the
624+
// ExecutionPlan.
625+
TEST_F(ProgramTest, GetOutputFlatteningEncodingWithMissingContainerMetaType) {
626+
// Build a minimal valid Program with an ExecutionPlan that has NO
627+
// container_meta_type. This would previously crash with a null pointer
628+
// dereference.
629+
flatbuffers::FlatBufferBuilder builder(1024);
630+
631+
// Create a minimal ExecutionPlan with name but NO container_meta_type
632+
auto plan_name = builder.CreateString("forward");
633+
634+
// Create empty vectors for required fields
635+
auto empty_values = builder.CreateVector(
636+
std::vector<flatbuffers::Offset<executorch_flatbuffer::EValue>>{});
637+
auto empty_inputs = builder.CreateVector(std::vector<int32_t>{});
638+
auto empty_outputs = builder.CreateVector(std::vector<int32_t>{});
639+
auto empty_chains = builder.CreateVector(
640+
std::vector<flatbuffers::Offset<executorch_flatbuffer::Chain>>{});
641+
auto empty_operators = builder.CreateVector(
642+
std::vector<flatbuffers::Offset<executorch_flatbuffer::Operator>>{});
643+
auto empty_delegates = builder.CreateVector(
644+
std::vector<
645+
flatbuffers::Offset<executorch_flatbuffer::BackendDelegate>>{});
646+
auto buffer_sizes = builder.CreateVector(std::vector<int64_t>{0});
647+
648+
// Build ExecutionPlan WITHOUT container_meta_type (it will be null)
649+
auto execution_plan = executorch_flatbuffer::CreateExecutionPlan(
650+
builder,
651+
plan_name,
652+
0, // container_meta_type = null (this is the vulnerability trigger)
653+
empty_values,
654+
empty_inputs,
655+
empty_outputs,
656+
empty_chains,
657+
empty_operators,
658+
empty_delegates,
659+
buffer_sizes);
660+
661+
auto execution_plans = builder.CreateVector(
662+
std::vector<flatbuffers::Offset<executorch_flatbuffer::ExecutionPlan>>{
663+
execution_plan});
664+
665+
// Create empty segment info
666+
auto empty_constant_buffer = builder.CreateVector(
667+
std::vector<flatbuffers::Offset<executorch_flatbuffer::Buffer>>{});
668+
auto empty_backend_data = builder.CreateVector(
669+
std::vector<flatbuffers::Offset<
670+
executorch_flatbuffer::BackendDelegateInlineData>>{});
671+
auto empty_segments = builder.CreateVector(
672+
std::vector<flatbuffers::Offset<executorch_flatbuffer::DataSegment>>{});
673+
674+
// Build the Program
675+
auto program = executorch_flatbuffer::CreateProgram(
676+
builder,
677+
0, // version
678+
execution_plans,
679+
empty_constant_buffer,
680+
empty_backend_data,
681+
empty_segments);
682+
683+
builder.Finish(program, executorch_flatbuffer::ProgramIdentifier());
684+
685+
// Copy to 16-byte aligned buffer (required by Program::load)
686+
alignas(16) uint8_t aligned_buffer[2048];
687+
ASSERT_LE(builder.GetSize(), sizeof(aligned_buffer));
688+
std::memcpy(aligned_buffer, builder.GetBufferPointer(), builder.GetSize());
689+
690+
// Load the malformed program
691+
BufferDataLoader data_loader(aligned_buffer, builder.GetSize());
692+
Result<Program> loaded_program =
693+
Program::load(&data_loader, Program::Verification::Minimal);
694+
695+
// Program should load successfully (the malformed data is valid FlatBuffer)
696+
ASSERT_EQ(loaded_program.error(), Error::Ok);
697+
698+
// Calling get_output_flattening_encoding should return an error,
699+
// NOT crash with null pointer dereference
700+
Result<const char*> encoding =
701+
loaded_program->get_output_flattening_encoding("forward");
702+
703+
// Should return InvalidProgram error due to missing container_meta_type
704+
EXPECT_EQ(encoding.error(), Error::InvalidProgram);
705+
}
706+
707+
// Test that get_output_flattening_encoding handles missing encoded_out_str
708+
TEST_F(ProgramTest, GetOutputFlatteningEncodingWithMissingEncodedOutStr) {
709+
flatbuffers::FlatBufferBuilder builder(2048);
710+
711+
// Create ContainerMetadata with encoded_inp_str but NO encoded_out_str
712+
auto inp_str = builder.CreateString("test_input");
713+
auto container_meta = executorch_flatbuffer::CreateContainerMetadata(
714+
builder,
715+
inp_str,
716+
0); // encoded_out_str = null
717+
718+
auto plan_name = builder.CreateString("forward");
719+
auto empty_values = builder.CreateVector(
720+
std::vector<flatbuffers::Offset<executorch_flatbuffer::EValue>>{});
721+
auto empty_inputs = builder.CreateVector(std::vector<int32_t>{});
722+
auto empty_outputs = builder.CreateVector(std::vector<int32_t>{});
723+
auto empty_chains = builder.CreateVector(
724+
std::vector<flatbuffers::Offset<executorch_flatbuffer::Chain>>{});
725+
auto empty_operators = builder.CreateVector(
726+
std::vector<flatbuffers::Offset<executorch_flatbuffer::Operator>>{});
727+
auto empty_delegates = builder.CreateVector(
728+
std::vector<
729+
flatbuffers::Offset<executorch_flatbuffer::BackendDelegate>>{});
730+
auto buffer_sizes = builder.CreateVector(std::vector<int64_t>{0});
731+
732+
auto execution_plan = executorch_flatbuffer::CreateExecutionPlan(
733+
builder,
734+
plan_name,
735+
container_meta, // container_meta_type exists
736+
empty_values,
737+
empty_inputs,
738+
empty_outputs,
739+
empty_chains,
740+
empty_operators,
741+
empty_delegates,
742+
buffer_sizes);
743+
744+
auto execution_plans = builder.CreateVector(
745+
std::vector<flatbuffers::Offset<executorch_flatbuffer::ExecutionPlan>>{
746+
execution_plan});
747+
748+
auto empty_constant_buffer = builder.CreateVector(
749+
std::vector<flatbuffers::Offset<executorch_flatbuffer::Buffer>>{});
750+
auto empty_backend_data = builder.CreateVector(
751+
std::vector<flatbuffers::Offset<
752+
executorch_flatbuffer::BackendDelegateInlineData>>{});
753+
auto empty_segments = builder.CreateVector(
754+
std::vector<flatbuffers::Offset<executorch_flatbuffer::DataSegment>>{});
755+
756+
auto program = executorch_flatbuffer::CreateProgram(
757+
builder,
758+
0,
759+
execution_plans,
760+
empty_constant_buffer,
761+
empty_backend_data,
762+
empty_segments);
763+
764+
builder.Finish(program, executorch_flatbuffer::ProgramIdentifier());
765+
766+
// Copy to 16-byte aligned buffer (required by Program::load)
767+
alignas(16) uint8_t aligned_buffer[2048];
768+
ASSERT_LE(builder.GetSize(), sizeof(aligned_buffer));
769+
std::memcpy(aligned_buffer, builder.GetBufferPointer(), builder.GetSize());
770+
771+
BufferDataLoader data_loader(aligned_buffer, builder.GetSize());
772+
Result<Program> loaded_program =
773+
Program::load(&data_loader, Program::Verification::Minimal);
774+
775+
ASSERT_EQ(loaded_program.error(), Error::Ok);
776+
777+
// Should return error for missing encoded_out_str, not crash
778+
Result<const char*> encoding =
779+
loaded_program->get_output_flattening_encoding("forward");
780+
781+
EXPECT_EQ(encoding.error(), Error::InvalidProgram);
782+
}

0 commit comments

Comments
 (0)