Skip to content

Commit fe760f3

Browse files
Initial changes to micro_allocator.cc, micro_allocator.h and micro_allocator_text.cc
1 parent cee9550 commit fe760f3

File tree

3 files changed

+185
-6
lines changed

3 files changed

+185
-6
lines changed

tensorflow/lite/micro/micro_allocator.cc

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,19 @@ TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
230230
TfLiteTensor* result) {
231231
TFLITE_DCHECK(result != nullptr);
232232

233+
// Validate buffer index before any dereference of the buffers vector.
234+
if (buffers == nullptr) {
235+
MicroPrintf("Model buffers vector is null");
236+
return kTfLiteError;
237+
}
238+
const uint32_t buffer_index = flatbuffer_tensor.buffer();
239+
if (buffer_index >= buffers->size()) {
240+
MicroPrintf(
241+
"Tensor references invalid buffer index %u, model has only %d buffers",
242+
buffer_index, buffers->size());
243+
return kTfLiteError;
244+
}
245+
233246
*result = {};
234247
// Make sure the serialized type is one we know how to deal with, and convert
235248
// it from a flatbuffer enum into a constant used by the kernel C API.
@@ -347,6 +360,20 @@ TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer(
347360
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
348361
TfLiteEvalTensor* result) {
349362
*result = {};
363+
364+
// Validate buffer index before any dereference of the buffers vector.
365+
if (buffers == nullptr) {
366+
MicroPrintf("Model buffers vector is null");
367+
return kTfLiteError;
368+
}
369+
const uint32_t buffer_index = flatbuffer_tensor.buffer();
370+
if (buffer_index >= buffers->size()) {
371+
MicroPrintf(
372+
"Tensor references invalid buffer index %u, model has only %d buffers",
373+
buffer_index, buffers->size());
374+
return kTfLiteError;
375+
}
376+
350377
// Make sure the serialized type is one we know how to deal with, and convert
351378
// it from a flatbuffer enum into a constant used by the kernel C API.
352379
TF_LITE_ENSURE_STATUS(
@@ -1104,14 +1131,41 @@ TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensorInternal() {
11041131
TfLiteStatus MicroAllocator::PopulateTfLiteTensorFromFlatbuffer(
11051132
const Model* model, TfLiteTensor* tensor, int tensor_index,
11061133
int subgraph_idx, bool allocate_temp) {
1107-
// TODO(b/162311891): This method serves as a stub to ensure quantized
1108-
// allocations in the tail can be recorded. Once the interpreter has APIs for
1109-
// accessing buffers on TfLiteEvalTensor this method can be dropped.
1134+
// Validate subgraph and tensor indices before dereferencing FlatBuffer
1135+
// vectors to avoid out-of-bounds access.
1136+
if (subgraph_idx < 0 ||
1137+
static_cast<size_t>(subgraph_idx) >= model->subgraphs()->size()) {
1138+
MicroPrintf("Invalid subgraph index %d, model has only %d subgraphs",
1139+
subgraph_idx, model->subgraphs()->size());
1140+
return kTfLiteError;
1141+
}
1142+
const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
1143+
if (subgraph == nullptr || subgraph->tensors() == nullptr) {
1144+
MicroPrintf("Subgraph %d has no tensors vector", subgraph_idx);
1145+
return kTfLiteError;
1146+
}
1147+
if (tensor_index < 0 ||
1148+
static_cast<size_t>(tensor_index) >= subgraph->tensors()->size()) {
1149+
MicroPrintf(
1150+
"Invalid tensor index %d for subgraph %d, subgraph has only %d tensors",
1151+
tensor_index, subgraph_idx, subgraph->tensors()->size());
1152+
return kTfLiteError;
1153+
}
1154+
1155+
const tflite::Tensor* flatbuffer_tensor =
1156+
subgraph->tensors()->Get(tensor_index);
1157+
1158+
// Buffer index validation is performed in InitializeTfLiteTensorFromFlatbuffer
1159+
// but we also ensure model->buffers() is non-null here for completeness.
1160+
if (model->buffers() == nullptr) {
1161+
MicroPrintf("Model buffers vector is null");
1162+
return kTfLiteError;
1163+
}
1164+
1165+
// Populate the TfLiteTensor fields from the flatbuffer.
11101166
return internal::InitializeTfLiteTensorFromFlatbuffer(
11111167
persistent_buffer_allocator_, non_persistent_buffer_allocator_,
1112-
allocate_temp,
1113-
*model->subgraphs()->Get(subgraph_idx)->tensors()->Get(tensor_index),
1114-
model->buffers(), tensor);
1168+
allocate_temp, *flatbuffer_tensor, model->buffers(), tensor);
11151169
}
11161170

11171171
TfLiteStatus MicroAllocator::CommitStaticMemoryPlan(

tensorflow/lite/micro/micro_allocator.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
4949
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
5050
TfLiteTensor* result);
5151

52+
// Initializes a TfLiteEvalTensor from a flatbuffer tensor and buffers vector.
53+
// This is the eval-phase counterpart to InitializeTfLiteTensorFromFlatbuffer.
54+
TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer(
55+
const tflite::Tensor& flatbuffer_tensor,
56+
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
57+
TfLiteEvalTensor* result);
58+
5259
// Holds placeholder information for a scratch buffer request from a kernel.
5360
// This struct is only used during the model prepare stage. Each request from a
5461
// kernel is stored in the head section. During the prepare stage, the head

tensorflow/lite/micro/micro_allocator_test.cc

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,4 +1384,122 @@ TF_LITE_MICRO_TEST(TestMultiSubgraphNumScratchAllocations) {
13841384
used_bytes + sizeof(tflite::ScratchBufferHandle) * 2);
13851385
}
13861386

1387+
// New tests validating invalid buffer index guards in tensor initialization
1388+
// and model allocation paths. These exercise issues #3077, #3076, #3075.
1389+
1390+
TF_LITE_MICRO_TEST(InitializeTfLiteTensorFromFlatbuffer_InvalidBufferIndexReturnsError) {
1391+
// Arena and allocator for temporary/persistent allocations used by init.
1392+
constexpr size_t arena_size = 1024;
1393+
uint8_t arena[arena_size];
1394+
tflite::SingleArenaBufferAllocator* simple_allocator =
1395+
tflite::SingleArenaBufferAllocator::Create(arena, arena_size);
1396+
1397+
// Build a flatbuffer Tensor that references a non-existent buffer index.
1398+
flatbuffers::FlatBufferBuilder builder;
1399+
const int32_t dims_data[] = {1};
1400+
auto dims_vec = builder.CreateVector(dims_data, 1);
1401+
// Use an invalid buffer index (>= buffers->size()).
1402+
const uint32_t kInvalidBufferIndex = 5;
1403+
auto name_str = builder.CreateString("invalid_buffer_tensor");
1404+
auto tensor_offset = tflite::CreateTensor(
1405+
builder, /*shape=*/dims_vec, tflite::TensorType_INT32,
1406+
/*buffer=*/kInvalidBufferIndex, /*name=*/name_str,
1407+
/*quantization=*/0, /*is_variable=*/false, /*sparsity=*/0);
1408+
builder.Finish(tensor_offset);
1409+
const tflite::Tensor* bad_tensor =
1410+
flatbuffers::GetRoot<tflite::Tensor>(builder.GetBufferPointer());
1411+
1412+
// Create a buffers vector with a single empty buffer.
1413+
const flatbuffers::Vector<flatbuffers::Offset<tflite::Buffer>>* buffers =
1414+
tflite::testing::CreateFlatbufferBuffers();
1415+
1416+
TfLiteTensor out_tensor;
1417+
// Expect kTfLiteError due to invalid buffer index.
1418+
TF_LITE_MICRO_EXPECT_EQ(
1419+
kTfLiteError,
1420+
tflite::internal::InitializeTfLiteTensorFromFlatbuffer(
1421+
simple_allocator, simple_allocator, /*allocate_temp=*/false,
1422+
*bad_tensor, buffers, &out_tensor));
1423+
1424+
simple_allocator->~SingleArenaBufferAllocator();
1425+
}
1426+
1427+
TF_LITE_MICRO_TEST(InitializeTfLiteEvalTensorFromFlatbuffer_InvalidBufferIndexReturnsError) {
1428+
// Build a flatbuffer Tensor that references a non-existent buffer index.
1429+
flatbuffers::FlatBufferBuilder builder;
1430+
const int32_t dims_data[] = {1};
1431+
auto dims_vec = builder.CreateVector(dims_data, 1);
1432+
const uint32_t kInvalidBufferIndex = 7;
1433+
auto name_str = builder.CreateString("invalid_eval_buffer_tensor");
1434+
auto tensor_offset = tflite::CreateTensor(
1435+
builder, /*shape=*/dims_vec, tflite::TensorType_INT32,
1436+
/*buffer=*/kInvalidBufferIndex, /*name=*/name_str,
1437+
/*quantization=*/0, /*is_variable=*/false, /*sparsity=*/0);
1438+
builder.Finish(tensor_offset);
1439+
const tflite::Tensor* bad_tensor =
1440+
flatbuffers::GetRoot<tflite::Tensor>(builder.GetBufferPointer());
1441+
1442+
// Create a buffers vector with a single empty buffer.
1443+
const flatbuffers::Vector<flatbuffers::Offset<tflite::Buffer>>* buffers =
1444+
tflite::testing::CreateFlatbufferBuffers();
1445+
1446+
TfLiteEvalTensor out_eval_tensor;
1447+
// Expect kTfLiteError due to invalid buffer index.
1448+
TF_LITE_MICRO_EXPECT_EQ(
1449+
kTfLiteError,
1450+
tflite::internal::InitializeTfLiteEvalTensorFromFlatbuffer(
1451+
*bad_tensor, buffers, &out_eval_tensor));
1452+
}
1453+
1454+
TF_LITE_MICRO_TEST(StartModelAllocation_FailsWhenSubgraphHasTensorWithInvalidBufferIndex) {
1455+
// Build a minimal model with a single tensor that references an invalid
1456+
// buffer index. Make it an output tensor to mimic output-table invalid case
1457+
// (#3075), but the allocator will validate all tensors equally, so this also
1458+
// simulates intermediate cases (#3076).
1459+
flatbuffers::FlatBufferBuilder fbb;
1460+
1461+
// One empty buffer at index 0 in Model.buffers().
1462+
flatbuffers::Offset<tflite::Buffer> buffers_arr[1] = {tflite::CreateBuffer(fbb)};
1463+
auto buffers_fb = fbb.CreateVector(buffers_arr, 1);
1464+
1465+
// Tensor with invalid buffer index.
1466+
const int32_t dims_data[] = {1};
1467+
auto dims_vec = fbb.CreateVector(dims_data, 1);
1468+
const uint32_t kInvalidBufferIndex = 3; // >= buffers_vec.size()
1469+
auto t_name = fbb.CreateString("out_tensor_invalid_buf");
1470+
auto tensor = tflite::CreateTensor(
1471+
fbb, dims_vec, tflite::TensorType_INT32, kInvalidBufferIndex, t_name,
1472+
/*quantization=*/0, /*is_variable=*/false, /*sparsity=*/0);
1473+
auto tensors_vec = fbb.CreateVector(&tensor, 1);
1474+
1475+
// Subgraph with the tensor as an output; no operators required.
1476+
const int32_t outputs_idx[] = {0};
1477+
auto outputs = fbb.CreateVector(outputs_idx, 1);
1478+
auto inputs = fbb.CreateVector<int32_t>({});
1479+
auto ops = fbb.CreateVector<flatbuffers::Offset<tflite::Operator>>({});
1480+
auto subgraph = tflite::CreateSubGraph(
1481+
fbb, tensors_vec, inputs, outputs, ops, fbb.CreateString("sg0"));
1482+
auto subgraphs = fbb.CreateVector(&subgraph, 1);
1483+
1484+
// Minimal model (no operator codes needed as there are no operators).
1485+
auto model = tflite::CreateModel(
1486+
fbb, /*version=*/TFLITE_SCHEMA_VERSION,
1487+
/*operator_codes=*/0, subgraphs, fbb.CreateString("invalid_buf_model"),
1488+
buffers_fb);
1489+
tflite::FinishModelBuffer(fbb, model);
1490+
const tflite::Model* m = flatbuffers::GetRoot<tflite::Model>(fbb.GetBufferPointer());
1491+
1492+
// Allocate an arena and create the allocator.
1493+
constexpr size_t arena_size = 2048;
1494+
uint8_t arena[arena_size];
1495+
tflite::MicroAllocator* allocator =
1496+
tflite::MicroAllocator::Create(arena, arena_size);
1497+
TF_LITE_MICRO_EXPECT(nullptr != allocator);
1498+
1499+
// StartModelAllocation should fail (return nullptr) because initializing
1500+
// any eval tensor with an invalid buffer index returns kTfLiteError.
1501+
tflite::SubgraphAllocations* subgraph_allocations = allocator->StartModelAllocation(m);
1502+
TF_LITE_MICRO_EXPECT(nullptr == subgraph_allocations);
1503+
}
1504+
13871505
TF_LITE_MICRO_TESTS_END

0 commit comments

Comments
 (0)