diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 265533397..7c5b4f024 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -27,8 +27,10 @@ if (OSSFUZZ) solc_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp + ../../EVMHost.cpp + SolidityEvmoneInterface.cpp ) - target_link_libraries(solc_ossfuzz PRIVATE libsolc evmasm) + target_link_libraries(solc_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone) set_target_properties(solc_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) add_executable(solc_mutator_ossfuzz diff --git a/test/tools/ossfuzz/SolidityEvmoneInterface.cpp b/test/tools/ossfuzz/SolidityEvmoneInterface.cpp index 954ae2bf0..9ca5bdf5a 100644 --- a/test/tools/ossfuzz/SolidityEvmoneInterface.cpp +++ b/test/tools/ossfuzz/SolidityEvmoneInterface.cpp @@ -65,6 +65,15 @@ optional SolidityCompilationFramework::compileContract() } } +optional SolidityCompilationFramework::noInputFunction() +{ + Json::Value const& contractABI = m_compiler.contractABI(m_compiler.lastContractName()); + for (auto const& functionABI: contractABI) + if (functionABI["inputs"].size() == 0) + return functionABI["name"].asString() + "()"; + return {}; +} + bool EvmoneUtility::zeroWord(uint8_t const* _result, size_t _length) { return _length == 32 && diff --git a/test/tools/ossfuzz/SolidityEvmoneInterface.h b/test/tools/ossfuzz/SolidityEvmoneInterface.h index 980eb9a84..52f35a78a 100644 --- a/test/tools/ossfuzz/SolidityEvmoneInterface.h +++ b/test/tools/ossfuzz/SolidityEvmoneInterface.h @@ -97,6 +97,14 @@ public: /// method identifiers in contract if compilation is successful, /// null value otherwise. std::optional compileContract(); + /// @returns name of a public/external method that does not contain + /// input parameters in its signature. + std::optional noInputFunction(); + /// @returns name of the last contract in source unit. + std::string lastContractName() + { + return m_compiler.lastContractName(); + } private: frontend::CompilerStack m_compiler; CompilerInput m_compilerInput; @@ -134,7 +142,27 @@ public: /// don't run into out of gas errors. The input field is copied from /// @param _input. static evmc_message initializeMessage(bytes const& _input); -private: + /// Sets method name to @param _methodName. + void methodName(std::string _methodName) + { + m_methodName = _methodName; + } + // Sets method name to @param _contractName. + void contractName(std::string _contractName) + { + m_contractName = _contractName; + m_compilationFramework.contractName(_contractName); + } + void lastContract() + { + m_contractName = m_compilationFramework.lastContractName(); + m_compilationFramework.contractName(m_contractName); + } + std::optional noInputFunction() + { + return m_compilationFramework.noInputFunction(); + } + /// @returns the result of the execution of the function whose /// keccak256 hash is @param _functionHash that is deployed at /// @param _deployedAddress in @param _hostContext. @@ -159,6 +187,8 @@ private: /// identifiers for methods in @param _contractName. std::optional compileContract(); +private: + /// EVM Host implementation solidity::test::EVMHost& m_evmHost; /// Solidity compilation framework diff --git a/test/tools/ossfuzz/solc_ossfuzz.cpp b/test/tools/ossfuzz/solc_ossfuzz.cpp index 620ef332b..dcc9bd6cf 100644 --- a/test/tools/ossfuzz/solc_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_ossfuzz.cpp @@ -18,43 +18,101 @@ #include +#include + #include +#include + +#include + #include using namespace solidity::frontend::test; +using namespace solidity::frontend; +using namespace solidity::test::fuzzer; +using namespace solidity::langutil; +using namespace solidity::test; using namespace std; // Prototype as we can't use the FuzzerInterface.h header. extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); +static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size <= 600) { string input(reinterpret_cast(_data), _size); + // TODO: Cannot fuzz tests containing libraries yet. + if (input.find("library") != string::npos) + return 0; + map sourceCode; try { + EVMVersion version; + EVMHost hostContext(version, evmone); + TestCaseReader t = TestCaseReader(std::istringstream(input)); sourceCode = t.sources().sources; - map settings = t.settings(); - bool compileViaYul = - settings.count("compileViaYul") && - (settings.at("compileViaYul") == "also" || settings.at("compileViaYul") == "true"); - bool optimize = settings.count("optimize") && settings.at("optimize") == "true"; - FuzzerUtil::testCompiler( + string contractName; + string methodName; + auto compilerSetting = OptimiserSettings::standard(); + CompilerInput cInput = { + version, sourceCode, - optimize, - /*_rand=*/static_cast(_size), - /*forceSMT=*/true, - compileViaYul + contractName, + compilerSetting, + {}, + false, + false + }; + EvmoneUtility evmoneUtil( + hostContext, + cInput, + contractName, + {}, + methodName ); + + auto compilerOutput = evmoneUtil.compileContract(); + if (!compilerOutput.has_value()) + return 0; + + optional noInputFunction = evmoneUtil.noInputFunction(); + if (noInputFunction.has_value()) + evmoneUtil.methodName(noInputFunction.value()); + else + return 0; + + auto deployResult = evmoneUtil.deployContract(compilerOutput->byteCode); + if (deployResult.status_code != EVMC_SUCCESS) + return 0; + + auto callResult = evmoneUtil.executeContract( + solidity::util::fromHex(compilerOutput->methodIdentifiersInContract[noInputFunction.value()].asString()), + deployResult.create_address + ); + + if (callResult.status_code != EVMC_SUCCESS) + return 0; + + solidity::bytes result; + for (size_t i = 0; i < callResult.output_size; i++) + result.push_back(callResult.output_data[i]); + + cout << solidity::util::toHex(result) << endl; } catch (runtime_error const&) { return 0; } + catch (solidity::langutil::UnimplementedFeatureError const&) + { + return 0; + } } return 0; }