Issue type
Bug
Have you reproduced the bug with TensorFlow Nightly?
No
Source
source
TensorFlow version
v2.18.0 (commit 6550e4b); code verified unchanged on current master
Custom code
Yes
OS platform and distribution
Fedora 43, x86_64
Mobile device
No response
Python version
No response
Bazel version
No response
GCC/compiler version
clang 21.1.8
CUDA/cuDNN version
No response
GPU model and memory
No response
Current behavior?
One note about testing against the nightly, the Python tf.lite.Interpreter uses the safe VerifyAndBuildFromBuffer path internally. The crash occurs via the C++ BuildFromBuffer/BuildFromFile API. I have source-verified the code at nightly commit 462d61f, the vulnerable code path is structurally identical to v2.18.0.
FlatBufferModel::BuildFromBuffer() calls ValidateModelBuffers() which follows FlatBuffer offsets without bounds checking. When the root table offset (first 4 bytes of a .tflite file) points past the buffer, flatbuffers::ReadScalar() reads out-of-bounds heap memory, crashing the process.
An 8-byte file is sufficient: \x18\x00\x00\x00TFL3
The root table offset is attacker-controlled (bytes 0-3), so the OOB read distance is steerable from 0 to 64KB+. ASan confirms heap-buffer-overflow at varying distances depending on the offset value.
BuildFromBuffer's source comment says "NOTE: this does NOT validate the buffer" and recommends VerifyAndBuildFromBuffer. However, TensorFlow's own canonical examples use the unverified API:
- tensorflow/lite/examples/label_image/label_image.cc uses BuildFromFile (no verification)
- tensorflow/lite/tools/benchmark/benchmark_tflite_model.cc uses BuildFromBuffer (no verification)
Expected behavior: BuildFromBuffer should either verify the FlatBuffer before accessing model fields, or ValidateModelBuffers should not follow unchecked offsets. At minimum, the official examples should use VerifyAndBuildFromFile.
Root cause: BuildFromAllocation() in model_builder_base.h calls ValidateModelBuffers() without first running flatbuffers::Verifier. ValidateModelBuffers() calls model_->buffers() which chains through GetVTable() → ReadScalar(), reading 4 bytes at buffer_base + root_table_offset, an attacker-controlled address.
Suggested fix: Add flatbuffers::Verifier check in BuildFromAllocation() before calling ValidateModelBuffers(), or deprecate the unverified Build* APIs in favor of VerifyAndBuild*.
Standalone code to reproduce the issue
# Generate 8-byte crash file
import struct
with open('/tmp/crash.tflite', 'wb') as f:
f.write(struct.pack('<I', 0x18) + b'TFL3')
# Reproduce via Python TFLite API (calls BuildFromFile internally)
import tensorflow as tf
interpreter = tf.lite.Interpreter(model_path='/tmp/crash.tflite')
# Crashes with SIGABRT or SIGSEGV before reaching this line
Relevant log output
==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7bda68fe00c8
at pc 0x00000058ff96 bp 0x7ffde66a68b0 sp 0x7ffde66a68a8
READ of size 4 at 0x7bda68fe00c8 thread T0
#0 flatbuffers::ReadScalar<int>() base.h:428
#1 flatbuffers::Table::GetVTable() table.h:30
#2 flatbuffers::Table::GetOptionalFieldOffset() table.h:37
#3 flatbuffers::Table::GetPointer<...>() table.h:52
#4 tflite::Model::buffers() schema_generated.h:16609
#5 FlatBufferModelBase::ValidateModelBuffers() model_builder_base.h:522
#6 FlatBufferModelBase::BuildFromAllocation() model_builder_base.h:356
#7 FlatBufferModelBase::BuildFromBuffer() model_builder_base.h:183
#8 LLVMFuzzerTestOneInput harness_tflite.cpp:32
0x7bda68fe00c8 is located 8 bytes before 8-byte region [0x7bda68fe00d0,0x7bda68fe00d8)
allocated here:
#0 operator new[](unsigned long)
Issue type
Bug
Have you reproduced the bug with TensorFlow Nightly?
No
Source
source
TensorFlow version
v2.18.0 (commit 6550e4b); code verified unchanged on current master
Custom code
Yes
OS platform and distribution
Fedora 43, x86_64
Mobile device
No response
Python version
No response
Bazel version
No response
GCC/compiler version
clang 21.1.8
CUDA/cuDNN version
No response
GPU model and memory
No response
Current behavior?
One note about testing against the nightly, the Python tf.lite.Interpreter uses the safe VerifyAndBuildFromBuffer path internally. The crash occurs via the C++ BuildFromBuffer/BuildFromFile API. I have source-verified the code at nightly commit 462d61f, the vulnerable code path is structurally identical to v2.18.0.
FlatBufferModel::BuildFromBuffer() calls ValidateModelBuffers() which follows FlatBuffer offsets without bounds checking. When the root table offset (first 4 bytes of a .tflite file) points past the buffer, flatbuffers::ReadScalar() reads out-of-bounds heap memory, crashing the process.
An 8-byte file is sufficient: \x18\x00\x00\x00TFL3
The root table offset is attacker-controlled (bytes 0-3), so the OOB read distance is steerable from 0 to 64KB+. ASan confirms heap-buffer-overflow at varying distances depending on the offset value.
BuildFromBuffer's source comment says "NOTE: this does NOT validate the buffer" and recommends VerifyAndBuildFromBuffer. However, TensorFlow's own canonical examples use the unverified API:
Expected behavior: BuildFromBuffer should either verify the FlatBuffer before accessing model fields, or ValidateModelBuffers should not follow unchecked offsets. At minimum, the official examples should use VerifyAndBuildFromFile.
Root cause: BuildFromAllocation() in model_builder_base.h calls ValidateModelBuffers() without first running flatbuffers::Verifier. ValidateModelBuffers() calls model_->buffers() which chains through GetVTable() → ReadScalar(), reading 4 bytes at buffer_base + root_table_offset, an attacker-controlled address.
Suggested fix: Add flatbuffers::Verifier check in BuildFromAllocation() before calling ValidateModelBuffers(), or deprecate the unverified Build* APIs in favor of VerifyAndBuild*.
Standalone code to reproduce the issue
Relevant log output
==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7bda68fe00c8 at pc 0x00000058ff96 bp 0x7ffde66a68b0 sp 0x7ffde66a68a8 READ of size 4 at 0x7bda68fe00c8 thread T0 #0 flatbuffers::ReadScalar<int>() base.h:428 #1 flatbuffers::Table::GetVTable() table.h:30 #2 flatbuffers::Table::GetOptionalFieldOffset() table.h:37 #3 flatbuffers::Table::GetPointer<...>() table.h:52 #4 tflite::Model::buffers() schema_generated.h:16609 #5 FlatBufferModelBase::ValidateModelBuffers() model_builder_base.h:522 #6 FlatBufferModelBase::BuildFromAllocation() model_builder_base.h:356 #7 FlatBufferModelBase::BuildFromBuffer() model_builder_base.h:183 #8 LLVMFuzzerTestOneInput harness_tflite.cpp:32 0x7bda68fe00c8 is located 8 bytes before 8-byte region [0x7bda68fe00d0,0x7bda68fe00d8) allocated here: #0 operator new[](unsigned long)