Out-of-Bounds Heap Read in `flatc --json`
- Dominant language
- C++
- Stars
- 26.5k
- Forks
- 3.7k
- PR merge metrics
- No merged PRs in 30d
Description
*AI was utilized for refinement and linguistic polishing of this report.*
## Summary
Out-of-Bounds Heap Read / Info Disclosure in `flatc --json` (Unverified Buffer Traversal)
**CWE:** CWE-125 (Out-of-bounds Read)
**Affected versions:** Tested on commit `e223d69b` (`flatc` version 25.12.19)
When converting a binary FlatBuffer to JSON, `flatbuffers::GenText()` trusts serialized vector length fields without first running the buffer through `flatbuffers::Verifier`.
Because `Vector::Get()` relies on `FLATBUFFERS_ASSERT` for bounds checking (which is stripped in release builds for performance), an attacker can provide a binary with a valid `file_identifier` but a corrupted vector length. `flatc` will accept the file normally and read far past the vector's allocation.
## Impact
In a standard release build, `flatc --json` silently reads unmapped or out-of-bounds heap memory and formats it as standard integer array elements in the resulting JSON file. This results in explicit Information Disclosure with no warning to the user. It can also cause a `SIGSEGV` if the read hits unmapped memory.
## Reproduction Steps
This requires no custom C++ code—only the stock `flatc` release build.
**1. Create a schema and valid data:**
```fbs
// oob_read.fbs
table Root { items: [int]; }
root_type Root;
file_identifier "EXMP";
```
```json
// valid.json
{ "items": [11, 22, 33] }
```
Compile to a valid 40-byte binary: `flatc -b oob_read.fbs valid.json`
**2. Corrupt the vector length (Python):**
```python
import struct
with open("valid.bin", "rb") as f: buf = bytearray(f.read())
# Standard FlatBuffer traversal to locate the vector
root_offset = struct.unpack_from("::operator[](unsigned int) const [vector.h:199]
#1 flatbuffers::JsonPrinter::PrintContainer<...>(...) [idl_gen_text.cpp:117]
#2 flatbuffers::JsonPrinter::PrintOffset(...)
#3 flatbuffers::JsonPrinter::GenStruct(...)
#4 flatbuffers::GenerateTextImpl(...) [idl_gen_text.cpp:383]
```
## Root Cause & Proposed Fix
The core issue is in `src/idl_gen_text.cpp`, where `GenText()` iterates `for (SizeT i = 0; i < size; i++)` based on the unverified `size` parameter from the buffer.
To harden `GenText()` against corrupt-buffer bugs without impacting the zero-copy performance of the core `Vector` headers, `GenText()` should explicitly verify buffer integrity before traversal.
**Suggested Patch:**
```diff
--- a/src/idl_gen_text.cpp
+++ b/src/idl_gen_text.cpp
@@ -418,6 +418,11 @@
const char* GenText(const Parser& parser, const void* flatbuffer,
std::string* _text) {
FLATBUFFERS_ASSERT(parser.root_struct_def_);
+
+ // Verify buffer integrity before traversal to prevent OOB reads.
+ flatbuffers::Verifier verifier(static_cast(flatbuffer), parser.builder_.GetSize());
+ if (!verifier.VerifyBuffer(nullptr)) return "buffer verification failed";
+
auto root = parser.opts.size_prefixed
```
[attachment.zip](https://github.com/user-attachments/files/26843607/attachment.zip)
Contributor guide
Assessment
This issue has not been assessed yet.