[fuzzer] Adds code generation target. (#8795)

* adds code generation fuzzer target.

* add buffer verification

* add table verification in codegen fuzzer

---------

Co-authored-by: Björn Harrtell <bjornharrtell@users.noreply.github.com>
This commit is contained in:
coder7695
2025-11-27 16:52:28 +01:00
committed by GitHub
parent 84f4b83d3e
commit 2b107e20c5
13 changed files with 495 additions and 10 deletions

View File

@@ -47,7 +47,7 @@ struct NsDefinition {
std::string path;
std::string filepath;
std::string symbolic_name;
const Namespace* ns;
const Namespace* ns = nullptr;
std::map<std::string, const Definition*> definitions;
};
@@ -258,7 +258,7 @@ class TsGenerator : public BaseGenerator {
nsDef.path = root;
nsDef.symbolic_name = file_name_;
nsDef.filepath = path_ + file_name_ + ".ts";
nsDef.ns = new Namespace();
nsDef.ns = parser_.empty_namespace_;
ns_defs_[nsDef.path] = nsDef;
}

View File

@@ -120,9 +120,67 @@ set(FlatBuffers_Library_SRCS
${FLATBUFFERS_DIR}/tests/64bit/test_64bit_bfbs_generated.h
)
set(FlatBuffers_Compiler_SRCS
${FLATBUFFERS_DIR}/src/idl_gen_binary.cpp
${FLATBUFFERS_DIR}/src/idl_gen_text.cpp
${FLATBUFFERS_DIR}/src/idl_gen_cpp.cpp
${FLATBUFFERS_DIR}/src/idl_gen_csharp.cpp
${FLATBUFFERS_DIR}/src/idl_gen_dart.cpp
${FLATBUFFERS_DIR}/src/idl_gen_kotlin.cpp
${FLATBUFFERS_DIR}/src/idl_gen_kotlin_kmp.cpp
${FLATBUFFERS_DIR}/src/idl_gen_go.cpp
${FLATBUFFERS_DIR}/src/idl_gen_java.cpp
${FLATBUFFERS_DIR}/src/idl_gen_ts.cpp
${FLATBUFFERS_DIR}/src/idl_gen_php.cpp
${FLATBUFFERS_DIR}/src/idl_gen_python.cpp
${FLATBUFFERS_DIR}/src/idl_gen_lobster.cpp
${FLATBUFFERS_DIR}/src/idl_gen_rust.cpp
${FLATBUFFERS_DIR}/src/idl_gen_fbs.cpp
${FLATBUFFERS_DIR}/src/idl_gen_grpc.cpp
${FLATBUFFERS_DIR}/src/idl_gen_json_schema.cpp
${FLATBUFFERS_DIR}/src/idl_gen_swift.cpp
${FLATBUFFERS_DIR}/src/file_name_saving_file_manager.cpp
${FLATBUFFERS_DIR}/src/file_binary_writer.cpp
${FLATBUFFERS_DIR}/src/file_writer.cpp
${FLATBUFFERS_DIR}/src/idl_namer.h
${FLATBUFFERS_DIR}/src/namer.h
${FLATBUFFERS_DIR}/src/flatc.cpp
# ${FLATBUFFERS_DIR}/src/flatc_main.cpp
${FLATBUFFERS_DIR}/src/bfbs_gen.h
${FLATBUFFERS_DIR}/src/bfbs_gen_lua.h
${FLATBUFFERS_DIR}/src/bfbs_gen_nim.h
${FLATBUFFERS_DIR}/src/bfbs_namer.h
${FLATBUFFERS_DIR}/include/codegen/idl_namer.h
${FLATBUFFERS_DIR}/include/codegen/namer.h
${FLATBUFFERS_DIR}/include/codegen/python.h
${FLATBUFFERS_DIR}/include/codegen/python.cc
${FLATBUFFERS_DIR}/include/flatbuffers/code_generators.h
${FLATBUFFERS_DIR}/src/binary_annotator.h
${FLATBUFFERS_DIR}/src/binary_annotator.cpp
${FLATBUFFERS_DIR}/src/annotated_binary_text_gen.h
${FLATBUFFERS_DIR}/src/annotated_binary_text_gen.cpp
${FLATBUFFERS_DIR}/src/bfbs_gen_lua.cpp
${FLATBUFFERS_DIR}/src/bfbs_gen_nim.cpp
${FLATBUFFERS_DIR}/src/code_generators.cpp
${FLATBUFFERS_DIR}/grpc/src/compiler/schema_interface.h
${FLATBUFFERS_DIR}/grpc/src/compiler/cpp_generator.h
${FLATBUFFERS_DIR}/grpc/src/compiler/cpp_generator.cc
${FLATBUFFERS_DIR}/grpc/src/compiler/go_generator.h
${FLATBUFFERS_DIR}/grpc/src/compiler/go_generator.cc
${FLATBUFFERS_DIR}/grpc/src/compiler/java_generator.h
${FLATBUFFERS_DIR}/grpc/src/compiler/java_generator.cc
${FLATBUFFERS_DIR}/grpc/src/compiler/python_generator.h
${FLATBUFFERS_DIR}/grpc/src/compiler/python_generator.cc
${FLATBUFFERS_DIR}/grpc/src/compiler/swift_generator.h
${FLATBUFFERS_DIR}/grpc/src/compiler/swift_generator.cc
${FLATBUFFERS_DIR}/grpc/src/compiler/ts_generator.h
${FLATBUFFERS_DIR}/grpc/src/compiler/ts_generator.cc
)
include_directories(${FLATBUFFERS_DIR}/include)
include_directories(${FLATBUFFERS_DIR}/tests)
include_directories(${FLATBUFFERS_DIR}/src)
include_directories(${FLATBUFFERS_DIR}/grpc)
add_library(flatbuffers_fuzzed STATIC ${FlatBuffers_Library_SRCS})
# Use PUBLIC to force 'fuzzer_config' for all dependent targets
@@ -156,6 +214,11 @@ target_link_libraries(flexverifier_fuzzer PRIVATE flatbuffers_fuzzed)
add_executable(monster_fuzzer flatbuffers_monster_fuzzer.cc)
target_link_libraries(monster_fuzzer PRIVATE flatbuffers_fuzzed)
add_executable(codegen_fuzzer flatbuffers_codegen_fuzzer.cc ${FlatBuffers_Compiler_SRCS})
target_link_libraries(codegen_fuzzer PRIVATE flatbuffers_fuzzed)
target_compile_definitions(codegen_fuzzer PRIVATE assert=fuzzer_assert_impl)
add_custom_command(
TARGET monster_fuzzer PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy

View File

@@ -0,0 +1,132 @@
"struct"
"table"
"enum"
"union"
"include"
"namespace"
"attribute"
"null"
"NULL"
"byte"
"int8"
"ubyte"
"uint8"
"bool"
"short"
"int16"
"ushort"
"uint16"
"int"
"int32"
"uint"
"uint32"
"float"
"float32"
"long"
"int64"
"ulong"
"uint64"
"double"
"float64"
"root_type"
"file_identifier"
"file_extension"
"rpc_service"
"streaming"
"idempotent"
"key"
"hash"
"fnv1_32"
"fnv1_64"
"fnv1a_32"
"fnv1a_64"
"deprecated"
"required"
"nested_flatbuffer"
"flexbuffer"
"native_inline"
"force_align"
"bit_flags"
"csharp_partial"
"private"
"cpp_type"
"cpp_ptr_type"
"cpp_ptr_type_get"
"{"
"}"
"["
"]"
"\""
"'"
"\\"
"//"
":"
","
" "
"\\n"
"\\r"
"/*"
"*/"
"true"
"false"
"null"
"\\u"
"\\b"
"\\f"
"\\t"
"."
"e"
"e+"
"e-"
"E"
"E+"
"E-"
"0x"
"p"
"a"
"b"
"Monster"
"pos"
"hp"
"name"
"weapons"
"damage"
"equipped_type"
"equipped"
"inventory"
"vector_of_longs"
"vector_of_doubles"
"test_type"
"test"
"test1"
"test2"
"test4"
"test3"
"test5"
"enemy"
"Weapon"
"Green"
"Red"
"Blue"
"testarrayofstring"
"testarrayofbools"
"testbool"
"flex"
"Color"
"Vec3"
"Stat"
"Referrable"
"Any"
"TestSimpleTableWithEnum"
"Race"
"LongEnum"
"Test"
"Ability"
"StructOfStructs"
"StructOfStructsOfStructs"
"TypeAliases"
"MonsterStorage"
"Store"
"Retrieve"
"GetMaxHitPoint"
"GetMinMaxHitPoints"

View File

@@ -0,0 +1,156 @@
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "flatbuffers/idl.h" // For Parser and generation functions
#include "test_init.h"
#include <memory>
#include <flatbuffers/flatc.h>
#include "idl_gen_cpp.h" // For C++ generator
#include <idl_gen_csharp.h>
#include <iostream>
#include "flatbuffers/code_generator.h"
#include <idl_gen_binary.h>
#include <idl_gen_dart.h>
#include <idl_gen_fbs.h>
#include <idl_gen_go.h>
#include <idl_gen_java.h>
#include <idl_gen_json_schema.h>
#include <idl_gen_kotlin.h>
#include <idl_gen_kotlin.h>
#include <idl_gen_lobster.h>
#include <bfbs_gen_lua.h>
#include <bfbs_gen_nim.h>
#include <idl_gen_python.h>
#include <idl_gen_php.h>
#include <idl_gen_rust.h>
#include <idl_gen_text.h>
#include <idl_gen_swift.h>
#include <idl_gen_ts.h>
static constexpr size_t kMinInputLength = 1;
static constexpr size_t kMaxInputLength = 16384;
static constexpr uint8_t flags_strict_json = 0x80;
static constexpr uint8_t flags_skip_unexpected_fields_in_json = 0x40;
static constexpr uint8_t flags_allow_non_utf8 = 0x20;
// Utility for test run.
OneTimeTestInit OneTimeTestInit::one_time_init_;
static const char* g_program_name = nullptr;
static void Warn(const flatbuffers::FlatCompiler* flatc,
const std::string& warn, bool show_exe_name) {
(void)flatc;
if (show_exe_name) {
printf("%s: ", g_program_name);
}
fprintf(stderr, "\nwarning:\n %s\n\n", warn.c_str());
}
static void Error(const flatbuffers::FlatCompiler* flatc,
const std::string& err, bool usage, bool show_exe_name) {
if (show_exe_name) {
printf("%s: ", g_program_name);
}
if (usage && flatc) {
fprintf(stderr, "%s\n", flatc->GetShortUsageString(g_program_name).c_str());
}
fprintf(stderr, "\nerror:\n %s\n\n", err.c_str());
exit(1);
}
namespace flatbuffers {
void LogCompilerWarn(const std::string& warn) {
Warn(static_cast<const flatbuffers::FlatCompiler*>(nullptr), warn, true);
}
void LogCompilerError(const std::string& err) {
Error(static_cast<const flatbuffers::FlatCompiler*>(nullptr), err, false,
true);
}
} // namespace flatbuffers
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Reserve one byte for Parser flags and one byte for repetition counter.
if (size < 3) return 0;
const uint8_t flags = data[0];
(void)data[1]; // reserved
data += 2;
size -= 2; // bypass
const std::string original(reinterpret_cast<const char*>(data), size);
auto input = std::string(original.c_str()); // until '\0'
if (input.size() < kMinInputLength || input.size() > kMaxInputLength)
return 0;
flatbuffers::IDLOptions opts;
opts.strict_json = (flags & flags_strict_json);
opts.skip_unexpected_fields_in_json =
(flags & flags_skip_unexpected_fields_in_json);
opts.allow_non_utf8 = (flags & flags_allow_non_utf8);
flatbuffers::Parser parser(opts);
// Guarantee 0-termination in the input.
auto parse_input = input.c_str();
// Parse the input schema
if (parser.Parse(parse_input)) {
parser.Serialize();
const uint8_t* buf = parser.builder_.GetBufferPointer();
flatbuffers::Verifier verifier(buf,
parser.builder_.GetSize());
TEST_EQ(true, reflection::VerifySchemaBuffer(verifier));
auto root = flatbuffers::GetRoot<flatbuffers::Table>(buf);
if (verifier.VerifyTableStart(buf) && root->VerifyTableStart(verifier)) {
if (parser.root_struct_def_) {
std::string json_output;
flatbuffers::GenText(parser, parser.builder_.GetBufferPointer(),
&json_output);
}
}
std::string temp_filename = "fuzzer_generated";
const std::string flatbuffers_version(flatbuffers::FLATBUFFERS_VERSION());
std::vector<std::unique_ptr<flatbuffers::CodeGenerator>> generators;
generators.emplace_back(flatbuffers::NewBinaryCodeGenerator());
generators.emplace_back(flatbuffers::NewCppCodeGenerator());
generators.emplace_back(flatbuffers::NewCSharpCodeGenerator());
generators.emplace_back(flatbuffers::NewDartCodeGenerator());
generators.emplace_back(flatbuffers::NewFBSCodeGenerator());
generators.emplace_back(flatbuffers::NewGoCodeGenerator());
generators.emplace_back(flatbuffers::NewJavaCodeGenerator());
generators.emplace_back(flatbuffers::NewJsonSchemaCodeGenerator());
generators.emplace_back(flatbuffers::NewKotlinCodeGenerator());
generators.emplace_back(flatbuffers::NewKotlinKMPCodeGenerator());
generators.emplace_back(flatbuffers::NewLobsterCodeGenerator());
generators.emplace_back(flatbuffers::NewLuaBfbsGenerator(flatbuffers_version));
generators.emplace_back(flatbuffers::NewNimBfbsGenerator(flatbuffers_version));
generators.emplace_back(flatbuffers::NewPythonCodeGenerator());
generators.emplace_back(flatbuffers::NewPhpCodeGenerator());
generators.emplace_back(flatbuffers::NewRustCodeGenerator());
generators.emplace_back(flatbuffers::NewTextCodeGenerator());
generators.emplace_back(flatbuffers::NewSwiftCodeGenerator());
generators.emplace_back(flatbuffers::NewTsCodeGenerator());
for (auto& gen : generators) {
auto p = gen.get();
std::string temp_path = "/tmp/";
auto status = p->GenerateCode(parser, temp_path, "fuzzer_generated");
if (status != flatbuffers::CodeGenerator::Status::OK) {
TEST_OUTPUT_LINE("GenerateCode failed %d", status);
}
// test gRPC code generation
auto grpc_status =
p->GenerateGrpcCode(parser, temp_path, "fuzzer_generated");
if (grpc_status != flatbuffers::CodeGenerator::Status::OK) {
TEST_OUTPUT_LINE("GenerateGrpcCode failed %d", grpc_status);
}
}
}
return 0;
}

View File

@@ -45,13 +45,20 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
auto parse_input = input.c_str();
// Check Parser.
parser.Parse(parse_input);
// TODO:
// Need to add additional checks for inputs passed Parse(parse_input)
// successfully:
// 1. Serialization to bfbs.
// 2. Generation of a default object.
// 3. Verification of the object using reflection.
// 3. Printing to json.
auto result = parser.Parse(parse_input);
if (result) {
parser.Serialize();
const uint8_t* buf = parser.builder_.GetBufferPointer();
flatbuffers::Verifier verifier(buf, parser.builder_.GetSize());
TEST_EQ(true, reflection::VerifySchemaBuffer(verifier));
auto root = flatbuffers::GetRoot<flatbuffers::Table>(buf);
if (verifier.VerifyTableStart(buf) && root->VerifyTableStart(verifier)) {
if (parser.root_struct_def_) {
std::string json_output;
flatbuffers::GenText(parser, buf, &json_output);
}
}
}
return 0;
}

View File

@@ -0,0 +1,11 @@
attribute "csharp_namespace";
namespace Test;
table Data (csharp_partial) {
value:int;
}
root_type Data;
file_identifier "TEST";

View File

@@ -0,0 +1,24 @@
namespace Example;
enum Status:byte { Active, Inactive, Pending }
struct Point {
x:int;
y:int;
}
table User {
id:uint;
name:string;
points:[Point];
status:Status = Active;
metadata:[string];
}
table Group {
name:string;
users:[User];
leader:User;
}
root_type Group;

View File

@@ -0,0 +1,6 @@
table Person {
name:string;
age:int;
}
root_type Person;

View File

@@ -0,0 +1,8 @@
enum Color:byte { Red, Green, Blue }
table Item {
name:string;
color:Color;
}
root_type Item;

View File

@@ -0,0 +1,33 @@
// Example IDL file for our monster's schema.
namespace MyGame.Sample;
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { Weapon } // Optionally add more tables.
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3;
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte];
color:Color = Blue;
weapons:[Weapon];
equipped:Equipment;
path:[Vec3];
}
table Weapon {
name:string;
damage:short;
}
root_type Monster;

View File

@@ -0,0 +1,14 @@
table Request {
id:int;
message:string;
}
table Response {
result:string;
}
rpc_service MyService {
DoSomething(Request):Response;
}
root_type Request;

View File

@@ -0,0 +1,11 @@
struct Vec2 {
x:float;
y:float;
}
table Object {
position:Vec2;
id:int;
}
root_type Object;

View File

@@ -0,0 +1,20 @@
namespace Test;
union Shape {
Circle,
Square,
}
table Circle {
radius: float;
}
table Square {
side: float;
}
table Root {
shape: Shape;
}
root_type Root;