diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index a799bd736..139c9a624 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -12,6 +12,7 @@ add_dependencies(ossfuzz if (OSSFUZZ) add_custom_target(ossfuzz_proto) add_dependencies(ossfuzz_proto + sol_import_proto_ossfuzz sol_proto_ossfuzz yul_proto_ossfuzz yul_proto_diff_ossfuzz @@ -124,6 +125,23 @@ if (OSSFUZZ) ) set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) target_compile_options(sol_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) + + add_executable(sol_import_proto_ossfuzz + SolImportProtoFuzzer.cpp + SolImportProtoConverter.cpp + solImportProto.pb.cc + ../fuzzer_common.cpp + ) + target_include_directories(sol_import_proto_ossfuzz PRIVATE + /usr/include/libprotobuf-mutator + ) + target_link_libraries(sol_import_proto_ossfuzz PRIVATE solidity libsolc + protobuf-mutator-libfuzzer.a + protobuf-mutator.a + protobuf.a + ) + set_target_properties(sol_import_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + target_compile_options(sol_import_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) else() add_library(solc_opt_ossfuzz solc_opt_ossfuzz.cpp diff --git a/test/tools/ossfuzz/SolImportProtoConverter.cpp b/test/tools/ossfuzz/SolImportProtoConverter.cpp new file mode 100644 index 000000000..56e5eb896 --- /dev/null +++ b/test/tools/ossfuzz/SolImportProtoConverter.cpp @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +using namespace solidity::test::solimportprotofuzzer; +using namespace std; + +map ProtoConverter::sourceCodeMap(Test const& _input) +{ + // Setup random number generator + m_randomGen = make_unique(_input.seed()); + m_numSources = _input.source_size(); + unsigned sourceIndex = 0; + for (auto const& source: _input.source()) + m_sourceMap["i" + to_string(sourceIndex++) + ".sol"] = visit(source); + return m_sourceMap; +} + +string ProtoConverter::visit(Source const& _source) +{ + // A source file may import itself and any of the + // other source files. + unsigned numImports = _source.num_imports() % (m_numSources + 1); + string sourceCode = {}; + set importSet; + for (unsigned i = 0; i < numImports; i++) + { + unsigned sourceIndex = randomNum() % m_numSources; + // Avoid duplicate imports + if (importSet.count(sourceIndex)) + continue; + else + importSet.insert(sourceIndex); + string sourceName = "i" + to_string(sourceIndex) + ".sol"; + string importName = "I" + to_string(sourceIndex); + sourceCode += "import \"" + sourceName + "\" as " + importName + ";\n"; + } + sourceCode += _source.code(); + boost::range::remove_erase_if(sourceCode, [=](char c) -> bool { + return !(std::isprint(c) || c == '\n'); + }); + return sourceCode; +} \ No newline at end of file diff --git a/test/tools/ossfuzz/SolImportProtoConverter.h b/test/tools/ossfuzz/SolImportProtoConverter.h new file mode 100644 index 000000000..668a5fbe0 --- /dev/null +++ b/test/tools/ossfuzz/SolImportProtoConverter.h @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +namespace solidity::test::solimportprotofuzzer +{ + +/// Random number generator that is seeded with a fuzzer +/// supplied unsigned integer. +struct SolRandomNumGenerator +{ + using RandomEngine = std::mt19937_64; + + explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {} + + /// @returns a pseudo random unsigned integer + unsigned operator()() + { + return m_random(); + } + + RandomEngine m_random; +}; + +class ProtoConverter +{ +public: + ProtoConverter() {} + ProtoConverter(ProtoConverter const&) = delete; + ProtoConverter(ProtoConverter&&) = delete; + std::map sourceCodeMap(Test const& _input); +private: + std::string visit(Source const& _source); + unsigned randomNum() + { + return (*m_randomGen)(); + } + + /// Number of source files declared in test file + unsigned m_numSources = 0; + /// Source code map to be passed to compiler stack + std::map m_sourceMap; + /// Smart pointer to a random number generator seeded by fuzzing input + std::unique_ptr m_randomGen; +}; +} \ No newline at end of file diff --git a/test/tools/ossfuzz/SolImportProtoFuzzer.cpp b/test/tools/ossfuzz/SolImportProtoFuzzer.cpp new file mode 100644 index 000000000..c8ea5f71c --- /dev/null +++ b/test/tools/ossfuzz/SolImportProtoFuzzer.cpp @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include + +#include + +#include +#include + +using namespace solidity::test::solimportprotofuzzer; +using namespace std; + +DEFINE_PROTO_FUZZER(Test const& _input) +{ + auto sourceMap = ProtoConverter{}.sourceCodeMap(_input); + if (char const* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) + { + // With libFuzzer binary run this to generate a YUL source file x.yul: + // PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input + ofstream of(dump_path); + for (const auto &[key, value]: sourceMap) + { + string header = "==== Source: " + key + " ====\n"; + of.write(header.data(), header.size()); + of.write(value.data(), value.size()); + of.write("\n", 1); + } + } + FuzzerUtil::testCompiler(sourceMap, false, _input.ByteSizeLong()); +} diff --git a/test/tools/ossfuzz/solImportProto.proto b/test/tools/ossfuzz/solImportProto.proto new file mode 100644 index 000000000..19af5b894 --- /dev/null +++ b/test/tools/ossfuzz/solImportProto.proto @@ -0,0 +1,30 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +syntax = "proto2"; + +message Source { + required uint64 num_imports = 1; + required string code = 2; +} + +message Test { + repeated Source source = 1; + required uint64 seed = 2; +} + +package solidity.test.solimportprotofuzzer;