diff --git a/src/idl_gen_ts.cpp b/src/idl_gen_ts.cpp index 83ca66efa..aed7d59f8 100644 --- a/src/idl_gen_ts.cpp +++ b/src/idl_gen_ts.cpp @@ -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 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; } diff --git a/tests/fuzzer/CMakeLists.txt b/tests/fuzzer/CMakeLists.txt index b0c4ae6d6..03a508628 100644 --- a/tests/fuzzer/CMakeLists.txt +++ b/tests/fuzzer/CMakeLists.txt @@ -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 diff --git a/tests/fuzzer/codegen_json.dict b/tests/fuzzer/codegen_json.dict new file mode 100644 index 000000000..a820353c3 --- /dev/null +++ b/tests/fuzzer/codegen_json.dict @@ -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" \ No newline at end of file diff --git a/tests/fuzzer/flatbuffers_codegen_fuzzer.cc b/tests/fuzzer/flatbuffers_codegen_fuzzer.cc new file mode 100644 index 000000000..c0270abd8 --- /dev/null +++ b/tests/fuzzer/flatbuffers_codegen_fuzzer.cc @@ -0,0 +1,156 @@ +#include +#include +#include +#include "flatbuffers/idl.h" // For Parser and generation functions +#include "test_init.h" +#include +#include +#include "idl_gen_cpp.h" // For C++ generator +#include +#include +#include "flatbuffers/code_generator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(nullptr), warn, true); +} +void LogCompilerError(const std::string& err) { + Error(static_cast(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(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(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> 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; +} diff --git a/tests/fuzzer/flatbuffers_parser_fuzzer.cc b/tests/fuzzer/flatbuffers_parser_fuzzer.cc index d1877ec27..01b4450c4 100644 --- a/tests/fuzzer/flatbuffers_parser_fuzzer.cc +++ b/tests/fuzzer/flatbuffers_parser_fuzzer.cc @@ -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(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; } diff --git a/tests/fuzzer/seed_codegen/attributes_test b/tests/fuzzer/seed_codegen/attributes_test new file mode 100644 index 000000000..b18d59371 --- /dev/null +++ b/tests/fuzzer/seed_codegen/attributes_test @@ -0,0 +1,11 @@ +attribute "csharp_namespace"; + +namespace Test; + +table Data (csharp_partial) { + value:int; +} + +root_type Data; + +file_identifier "TEST"; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/complex_schema b/tests/fuzzer/seed_codegen/complex_schema new file mode 100644 index 000000000..e984cb311 --- /dev/null +++ b/tests/fuzzer/seed_codegen/complex_schema @@ -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; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/empty_namespace b/tests/fuzzer/seed_codegen/empty_namespace new file mode 100644 index 000000000..63e2bf9b2 --- /dev/null +++ b/tests/fuzzer/seed_codegen/empty_namespace @@ -0,0 +1,6 @@ +table Person { + name:string; + age:int; +} + +root_type Person; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/enum_test b/tests/fuzzer/seed_codegen/enum_test new file mode 100644 index 000000000..e60a03060 --- /dev/null +++ b/tests/fuzzer/seed_codegen/enum_test @@ -0,0 +1,8 @@ +enum Color:byte { Red, Green, Blue } + +table Item { + name:string; + color:Color; +} + +root_type Item; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/monster_sample b/tests/fuzzer/seed_codegen/monster_sample new file mode 100644 index 000000000..31e0ec616 --- /dev/null +++ b/tests/fuzzer/seed_codegen/monster_sample @@ -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; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/rpc_test b/tests/fuzzer/seed_codegen/rpc_test new file mode 100644 index 000000000..7af1ad321 --- /dev/null +++ b/tests/fuzzer/seed_codegen/rpc_test @@ -0,0 +1,14 @@ +table Request { + id:int; + message:string; +} + +table Response { + result:string; +} + +rpc_service MyService { + DoSomething(Request):Response; +} + +root_type Request; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/struct_test b/tests/fuzzer/seed_codegen/struct_test new file mode 100644 index 000000000..f6f2853a6 --- /dev/null +++ b/tests/fuzzer/seed_codegen/struct_test @@ -0,0 +1,11 @@ +struct Vec2 { + x:float; + y:float; +} + +table Object { + position:Vec2; + id:int; +} + +root_type Object; \ No newline at end of file diff --git a/tests/fuzzer/seed_codegen/test_union b/tests/fuzzer/seed_codegen/test_union new file mode 100644 index 000000000..1e3f1f899 --- /dev/null +++ b/tests/fuzzer/seed_codegen/test_union @@ -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; \ No newline at end of file