Skip to content

Commit 85b6676

Browse files
fix(runtime): address review feedback on null overload logging and named data errors
- Inline operator overload null check in ET_LOG to prevent unused variable warnings when logging is compiled out. - Return InvalidExternalData for keyless or null named data entries in get_named_data and get_key. - Simplify get_num_keys and remove unreachable null check branch. - Add test coverage for keyless named data entries in flat_tensor_data_map_test. - Add test coverage for kernel failure with null operator overload in program_validation_test.
1 parent a880b61 commit 85b6676

5 files changed

Lines changed: 144 additions & 48 deletions

File tree

‎extension/flat_tensor/flat_tensor_data_map.cpp‎

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,16 @@ Result<const flat_tensor_flatbuffer::NamedData*> get_named_data(
6464
}
6565
for (flatbuffers::uoffset_t i = 0; i < named_data->size(); ++i) {
6666
const auto* entry = named_data->Get(i);
67-
if (entry == nullptr || entry->key() == nullptr) {
68-
continue;
69-
}
67+
ET_CHECK_OR_RETURN_ERROR(
68+
entry != nullptr && entry->key() != nullptr,
69+
InvalidExternalData,
70+
"Named data entry or key at index %u is null. Malformed PTD file.",
71+
i);
7072
if (key.size() == entry->key()->size() &&
71-
std::strncmp(
72-
entry->key()->c_str(),
73-
key.data(),
74-
entry->key()->size()) == 0) {
75-
const auto* found = entry;
73+
std::strncmp(entry->key()->c_str(), key.data(), entry->key()->size()) ==
74+
0) {
7675
// Validate the named_data.
77-
const flatbuffers::uoffset_t segment_index = found->segment_index();
76+
const flatbuffers::uoffset_t segment_index = entry->segment_index();
7877
ET_CHECK_OR_RETURN_ERROR(
7978
segment_index < segments->size(),
8079
InvalidExternalData,
@@ -97,7 +96,7 @@ Result<const flat_tensor_flatbuffer::NamedData*> get_named_data(
9796
segments->Get(segment_index)->offset(),
9897
segments->Get(segment_index)->size(),
9998
segment_data_size);
100-
return found;
99+
return entry;
101100
}
102101
}
103102
return Error::NotFound;
@@ -252,35 +251,23 @@ ET_NODISCARD Error FlatTensorDataMap::load_data_into(
252251
}
253252

254253
ET_NODISCARD Result<uint32_t> FlatTensorDataMap::get_num_keys() const {
255-
if (flat_tensor_ == nullptr || flat_tensor_->named_data() == nullptr) {
256-
return 0;
257-
}
258254
return flat_tensor_->named_data()->size();
259255
}
260256

261257
ET_NODISCARD Result<const char*> FlatTensorDataMap::get_key(
262258
uint32_t index) const {
263-
Result<uint32_t> num_keys_res = get_num_keys();
264-
if (!num_keys_res.ok()) {
265-
return num_keys_res.error();
266-
}
267-
uint32_t num_keys = num_keys_res.get();
259+
const auto* named_data = flat_tensor_->named_data();
268260
ET_CHECK_OR_RETURN_ERROR(
269-
index < num_keys,
261+
index < named_data->size(),
270262
InvalidArgument,
271263
"Index %u out of range of size %u",
272264
index,
273-
num_keys);
274-
const auto* named_data = flat_tensor_->named_data();
275-
ET_CHECK_OR_RETURN_ERROR(
276-
named_data != nullptr,
277-
InvalidExternalData,
278-
"Named data is null");
265+
named_data->size());
279266
const auto* entry = named_data->Get(index);
280267
ET_CHECK_OR_RETURN_ERROR(
281268
entry != nullptr && entry->key() != nullptr,
282269
InvalidExternalData,
283-
"Named data entry or key at index %u is null",
270+
"Named data entry or key at index %u is null. Malformed PTD file.",
284271
index);
285272
return entry->key()->c_str();
286273
}

‎extension/flat_tensor/test/flat_tensor_data_map_test.cpp‎

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,6 @@ TEST_F(FlatTensorDataMapTest, GetKeys) {
134134
// Check get_key fails when out of bounds.
135135
Result<const char*> key2_res = data_map->get_key(2);
136136
EXPECT_EQ(key2_res.error(), Error::InvalidArgument);
137-
EXPECT_EQ(
138-
data_map->get_key(std::numeric_limits<uint32_t>::max()).error(),
139-
Error::InvalidArgument);
140137
}
141138

142139
TEST_F(FlatTensorDataMapTest, LoadInto) {
@@ -211,7 +208,8 @@ struct SegmentSpec {
211208
// test loader, so large source offsets do not require a large allocation.
212209
std::vector<uint8_t> CreateDataWithVersion(
213210
uint32_t version,
214-
const SegmentSpec* segment = nullptr) {
211+
const SegmentSpec* segment = nullptr,
212+
bool with_null_key = false) {
215213
flatbuffers::FlatBufferBuilder builder;
216214

217215
std::vector<flatbuffers::Offset<flat_tensor_flatbuffer::DataSegment>>
@@ -226,13 +224,16 @@ std::vector<uint8_t> CreateDataWithVersion(
226224
executorch_flatbuffer::ScalarType::FLOAT,
227225
builder.CreateVector(sizes),
228226
builder.CreateVector(dim_order));
229-
segments.push_back(flat_tensor_flatbuffer::CreateDataSegment(
230-
builder, segment->offset, segment->size));
231-
named_data.push_back(flat_tensor_flatbuffer::CreateNamedData(
232-
builder,
233-
builder.CreateString(kWideTensorKey),
234-
/*segment_index=*/0,
235-
tensor_layout));
227+
segments.push_back(
228+
flat_tensor_flatbuffer::CreateDataSegment(
229+
builder, segment->offset, segment->size));
230+
named_data.push_back(
231+
flat_tensor_flatbuffer::CreateNamedData(
232+
builder,
233+
with_null_key ? flatbuffers::Offset<flatbuffers::String>()
234+
: builder.CreateString(kWideTensorKey),
235+
/*segment_index=*/0,
236+
tensor_layout));
236237
}
237238

238239
auto flat_tensor = flat_tensor_flatbuffer::CreateFlatTensor(
@@ -387,6 +388,43 @@ TEST_F(FlatTensorDataMapTest, NewerSchemaVersionFailsToLoad) {
387388
EXPECT_EQ(data_map.error(), Error::InvalidExternalData);
388389
}
389390

391+
TEST_F(FlatTensorDataMapTest, KeylessNamedDataReturnsInvalidExternalData) {
392+
constexpr SegmentSpec kSegment{0, sizeof(float), sizeof(float)};
393+
std::vector<uint8_t> data = CreateDataWithVersion(
394+
FlatTensorDataMap::kMaxSupportedSchemaVersion,
395+
&kSegment,
396+
/*with_null_key=*/true);
397+
398+
alignas(std::max_align_t) std::array<uint8_t, 1024> aligned_buffer{};
399+
ASSERT_LE(data.size(), aligned_buffer.size());
400+
std::memcpy(aligned_buffer.data(), data.data(), data.size());
401+
402+
Result<FlatTensorHeader> header =
403+
FlatTensorHeader::Parse(aligned_buffer.data(), data.size());
404+
ASSERT_TRUE(header.ok());
405+
const uint64_t absolute_offset = header->segment_base_offset;
406+
const float segment_data = 1.0f;
407+
WideOffsetDataLoader loader(
408+
aligned_buffer.data(),
409+
data.size(),
410+
reinterpret_cast<const uint8_t*>(&segment_data),
411+
sizeof(segment_data),
412+
absolute_offset + sizeof(segment_data));
413+
414+
Result<FlatTensorDataMap> data_map = FlatTensorDataMap::load(&loader);
415+
ASSERT_TRUE(data_map.ok());
416+
417+
// get_key for the keyless entry must fail with InvalidExternalData.
418+
EXPECT_EQ(data_map->get_key(0).error(), Error::InvalidExternalData);
419+
420+
// Linear search in get_data and get_tensor_layout must encounter the keyless
421+
// entry and fail with InvalidExternalData instead of silently skipping it.
422+
EXPECT_EQ(data_map->get_data("any_key").error(), Error::InvalidExternalData);
423+
EXPECT_EQ(
424+
data_map->get_tensor_layout("any_key").error(),
425+
Error::InvalidExternalData);
426+
}
427+
390428
TEST_F(FlatTensorDataMapTest, RejectsOutOfBoundsRootOffset) {
391429
// A valid file loads.
392430
Result<FreeableBuffer> valid = data_map_loader_->load(

‎runtime/executor/method.cpp‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,21 +1732,18 @@ Error Method::execute_instruction() {
17321732
// checked at init time.
17331733
auto op_index = instruction->instr_args_as_KernelCall()->op_index();
17341734
ET_UNUSED auto op = serialization_plan_->operators()->Get(op_index);
1735-
const char* const op_name =
1736-
(op != nullptr && op->name() != nullptr) ? op->name()->c_str() : "unknown";
1737-
const bool has_overload =
1738-
op != nullptr && op->overload() != nullptr && op->overload()->size() > 0;
1739-
const char* const op_overload =
1740-
has_overload ? op->overload()->c_str() : "";
17411735
ET_LOG(
17421736
Error,
17431737
"KernelCall failed at instruction %" ET_PRIsize_t ":%" ET_PRIsize_t
17441738
" in operator %s%s%s: 0x%x",
17451739
step_state_.chain_idx,
17461740
step_state_.instr_idx,
1747-
op_name,
1748-
has_overload ? "." : "",
1749-
op_overload,
1741+
op->name()->c_str(),
1742+
(op->overload() != nullptr && op->overload()->size() > 0) ? "."
1743+
: "",
1744+
(op->overload() != nullptr && op->overload()->size() > 0)
1745+
? op->overload()->c_str()
1746+
: "",
17501747
(unsigned int)err);
17511748
for (size_t i = 0; i < args.size(); ++i) {
17521749
ET_LOG(

‎runtime/executor/test/CMakeLists.txt‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,19 @@ target_include_directories(
215215
"${EXECUTORCH_ROOT}/third-party/flatbuffers/include"
216216
)
217217

218+
et_cxx_test(
219+
program_validation_test SOURCES program_validation_test.cpp EXTRA_LIBS
220+
extension_data_loader program_schema
221+
)
222+
add_dependencies(program_validation_test generated_pte_files)
223+
set_property(TEST program_validation_test PROPERTY ENVIRONMENT ${test_env})
224+
225+
target_include_directories(
226+
program_validation_test
227+
PRIVATE "${CMAKE_INSTALL_PREFIX}/schema/include"
228+
"${EXECUTORCH_ROOT}/third-party/flatbuffers/include"
229+
)
230+
218231
et_cxx_test(
219232
kernel_resolution_test SOURCES kernel_resolution_test.cpp EXTRA_LIBS
220233
extension_data_loader

‎runtime/executor/test/program_validation_test.cpp‎

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
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

Comments
 (0)