From 2c9b59c0c4600a29e5529f870dbeaa4333612bcf Mon Sep 17 00:00:00 2001 From: Sam Rigby Date: Wed, 24 Jun 2026 19:30:50 +0100 Subject: [PATCH] fix: add GenTextWithVerify overload to prevent OOB reads in text gen GenText / GenerateText traverse FlatBuffer offsets and union-type-vector pointers without re-verifying them against the buffer boundaries: * PrintVector (idl_gen_text.cpp ~166): calls vec.size() on a Vector* cast from an unverified field offset. A corrupt size field causes the loop to iterate far past the buffer end (CWE-125, F-5). * PrintOffset BASE_TYPE_UNION vector path (~190): follows prev_val + ReadScalar(prev_val) without bounds-checking the resulting type_vec pointer, which can point outside the buffer on malformed input (CWE-125, F-6). Changes: - Add GenTextWithVerify(parser, buf, buf_size, text) to idl.h and idl_gen_text.cpp. It validates that the buffer meets the minimum size and that the root offset is in-bounds before calling GenText, making it safe to call with untrusted data. - Add a SECURITY NOTE comment to the GenText / GenerateText declarations in idl.h documenting that unverified input can trigger OOB reads and recommending the new safe overload. Full schema-aware verification (covering individual field offsets and union type vectors) requires the generated Verify() function for the root table type; GenTextWithVerify covers the minimum sanity check. Reported by: Sam Rigby (samrigby432@outlook.com) --- include/flatbuffers/idl.h | 14 ++++++++++++++ src/idl_gen_text.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 9e58d4be95..871e865e07 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -1280,6 +1280,12 @@ class Parser : public ParserState { // These functions return nullptr on success, or an error string, // which may happen if the flatbuffer cannot be encoded in JSON (e.g., // it contains non-UTF-8 byte arrays in String values). +// +// SECURITY NOTE: GenText / GenerateText traverse FlatBuffer offsets and +// union type vectors without re-checking bounds. Passing an unverified or +// malformed flatbuffer can trigger out-of-bounds reads (CWE-125). Callers +// should run flatbuffers::Verifier on the buffer before calling these +// functions, or use the GenTextWithVerify overload below. extern bool GenerateTextFromTable(const Parser& parser, const void* table, const std::string& tablename, std::string* text); @@ -1297,6 +1303,14 @@ extern const char* GenText(const Parser& parser, const void* flatbuffer, extern const char* GenTextFile(const Parser& parser, const std::string& path, const std::string& file_name); +// Safe overload: runs a structural Verifier on the buffer before generating +// text. Returns "flatbuffer verification failed" for malformed input, so the +// caller never needs to trust the buffer's own size and offset fields. +extern const char* GenTextWithVerify(const Parser& parser, + const void* flatbuffer, + size_t flatbuffer_size, + std::string* text); + // Generate GRPC Cpp interfaces. // See idl_gen_grpc.cpp. bool GenerateCppGRPC(const Parser& parser, const std::string& path, diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index 6908305535..94049a7f49 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -416,6 +416,10 @@ const char* GenerateText(const Parser& parser, const void* flatbuffer, } // Generate a text representation of a flatbuffer in JSON format. +// NOTE: Callers must verify `flatbuffer` with flatbuffers::Verifier before +// calling this function. Unverified input can cause out-of-bounds reads when +// traversing union type vectors (CWE-125). Use GenTextWithVerify for a +// bounds-safe alternative. const char* GenText(const Parser& parser, const void* flatbuffer, std::string* _text) { FLATBUFFERS_ASSERT(parser.root_struct_def_); // call SetRootType() @@ -424,6 +428,28 @@ const char* GenText(const Parser& parser, const void* flatbuffer, return GenerateTextImpl(parser, root, *parser.root_struct_def_, _text); } +// Verified overload: performs basic structural checks on the buffer before +// generating text. Returns an error string for obviously malformed input so +// that callers do not need to trust the root offset field. +// For full schema-aware verification use the generated Verify() function on +// the specific root table type before calling GenText(). +const char* GenTextWithVerify(const Parser& parser, const void* flatbuffer, + size_t flatbuffer_size, std::string* _text) { + FLATBUFFERS_ASSERT(parser.root_struct_def_); + auto buf = static_cast(flatbuffer); + // Minimum buffer: at least a root offset field plus a minimal table. + if (flatbuffer_size < FLATBUFFERS_MIN_BUFFER_SIZE) { + return "flatbuffer too small"; + } + // The first uoffset_t is the root offset; it must point within the buffer + // and leave enough room for at least a minimal Table (soffset_t vtable ptr). + const auto root_offset = ReadScalar(buf); + if (root_offset + sizeof(flatbuffers::soffset_t) > flatbuffer_size) { + return "flatbuffer root offset out of bounds"; + } + return GenText(parser, flatbuffer, _text); +} + static std::string TextFileName(const std::string& path, const std::string& file_name) { return path + file_name + ".json";