diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 5840741db..1de27af84 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -2,6 +2,7 @@ add_custom_target(ossfuzz) add_dependencies(ossfuzz solc_ossfuzz bytecode_optimizer_ossfuzz + solc_reverse_ossfuzz solc_mutator_ossfuzz const_opt_ossfuzz strictasm_diff_ossfuzz @@ -35,6 +36,22 @@ if (OSSFUZZ) ) target_link_libraries(solc_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone abicoder gmp.a) set_target_properties(solc_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + target_include_directories(solc_ossfuzz PRIVATE + /home/bhargava/work/github/solidity/deps/libabicoder + ) + set_target_properties(solc_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + + add_executable(solc_reverse_ossfuzz + solc_reverse_ossfuzz.cpp + ValueGenerator.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + ../../EVMHost.cpp + SolidityEvmoneInterface.cpp + ../../libsolidity/util/ContractABIUtils.cpp + ) + target_link_libraries(solc_reverse_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone abicoder gmp.a) + set_target_properties(solc_reverse_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) add_executable(bytecode_optimizer_ossfuzz BytecodeOptimizerFuzzer.cpp diff --git a/test/tools/ossfuzz/solc_reverse_ossfuzz.cpp b/test/tools/ossfuzz/solc_reverse_ossfuzz.cpp new file mode 100644 index 000000000..36013ad1b --- /dev/null +++ b/test/tools/ossfuzz/solc_reverse_ossfuzz.cpp @@ -0,0 +1,298 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +#include + +#include + +#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()}; +static constexpr size_t abiCoderHeapSize = 1024 * 512; + +namespace +{ +string abiEncoding( + Json::Value const& _functionABI, + map> _addressSelectors, + Json::Value const& _methodIdentifiers +) +{ + string abiTypeString; + string abiValueString; + tie(abiTypeString, abiValueString) = ValueGenerator{ + _functionABI["inputs"], + 0, + std::move(_addressSelectors) + }.type(); + string encodedData; + // A function with inputs must contain type names within + // parentheses. + bool functionWithInputs = abiTypeString != "()"; + string functionSignature = _functionABI["name"].asString() + abiTypeString; + cout << functionSignature << endl; + string encoding = _methodIdentifiers[functionSignature].asString(); + if (functionWithInputs) + { + abicoder::ABICoder coder(abiCoderHeapSize); + auto [status, data] = coder.encode(abiTypeString, abiValueString); + cout << abiTypeString << endl; + cout << abiValueString << endl; + solAssert(status, "Isabelle coder: failed."); + encoding += data.substr(2, data.size()); + } + return encoding; +} +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) +{ + string input(reinterpret_cast(_data), _size); + regex re = regex("library\\s*(\\w+)\\s*\\{"); + smatch matches; + std::string libraryName; + auto match = regex_search(input, matches, re); + if (match && matches[1].matched) + libraryName = matches[1].str(); + + map sourceCode; + try + { + EVMVersion version; + EVMHost hostContext(version, evmone); + + TestCaseReader t = TestCaseReader(std::istringstream(input)); + sourceCode = t.sources().sources; + string contractName; + string methodName; + auto compilerSetting = OptimiserSettings::standard(); + CompilerInput cInput = { + version, + sourceCode, + contractName, + compilerSetting, + {}, + true, + true + }; + EvmoneUtility evmoneUtil( + hostContext, + cInput, + contractName, + libraryName, + methodName + ); + + map> addressSelector; + // Add pre-compiles + for (auto const& account: hostContext.accounts) + addressSelector[EVMHost::convertFromEVMC(account.first)] = {}; + + optional libraryInfo; + vector libraryFunctionSelectors; + if (!libraryName.empty()) { + cout << "Deploying library" << endl; + libraryInfo = evmoneUtil.compileAndDeployLibrary(); + if (!libraryInfo.has_value() || get<0>(libraryInfo.value()) != EVMC_SUCCESS) + return 0; + // Add library functions to address selector + libraryFunctionSelectors = get<2>(libraryInfo.value()); + if (!libraryFunctionSelectors.empty()) + addressSelector[get<1>(libraryInfo.value())] = libraryFunctionSelectors; + cout << "Deployed" << endl; + } + + auto compilerOutput = evmoneUtil.compileContract(); + if (!compilerOutput.has_value()) + return 0; + + auto r = evmoneUtil.randomFunction(_size); + if (!r.has_value()) + return 0; + + auto deployResult = evmoneUtil.deployContract(compilerOutput->byteCode); + if (deployResult.status_code != EVMC_SUCCESS) + return 0; + + // Record function selectors in contract + vector functionSelectors; + for (auto const& item: compilerOutput->methodIdentifiersInContract) + functionSelectors.push_back(item.asString()); + + // Add deployed contract to list of address literals. + addressSelector[EVMHost::convertFromEVMC(deployResult.create_address)] = functionSelectors; + + string encodedData = abiEncoding(r.value(), addressSelector, + compilerOutput->methodIdentifiersInContract); + auto callResult = evmoneUtil.executeContract( + solidity::util::fromHex(encodedData), + deployResult.create_address + ); + + if (callResult.status_code != EVMC_SUCCESS) { + cout << "New code gen call failed with status code: " + << callResult.status_code + << endl; + return 0; + } + + solidity::bytes result; + for (size_t i = 0; i < callResult.output_size; i++) + result.push_back(callResult.output_data[i]); + + EVMHostPrinter p(hostContext, deployResult.create_address); + ostringstream oldCodeGen; + oldCodeGen << p.state(); + + // Reset call state post old code gen run + hostContext.reset(); + addressSelector.clear(); + bool libraryFunctions = !libraryFunctionSelectors.empty(); + libraryFunctionSelectors.clear(); + evmoneUtil.optSetting(compilerSetting); + evmoneUtil.viaIR(false); + + // Add pre-compiles post reset + for (auto const& account: hostContext.accounts) + addressSelector[EVMHost::convertFromEVMC(account.first)] = {}; + + optional libraryInfoNew; + if (!libraryName.empty()) + { + libraryInfoNew = evmoneUtil.compileAndDeployLibrary(); + solAssert( + libraryInfoNew.has_value() && get<0>(libraryInfoNew.value()) == EVMC_SUCCESS, + "Deploying library failed for the second time" + ); + libraryFunctionSelectors = get<2>(libraryInfoNew.value()); + // Library functions in both runs should either be both empty or + // both non-empty. + solAssert( + ( + (libraryFunctions && !libraryFunctionSelectors.empty()) || + (!libraryFunctions && libraryFunctionSelectors.empty()) + ), + "Library function mismatch." + ); + if (!libraryFunctionSelectors.empty()) + addressSelector[get<1>(libraryInfoNew.value())] = libraryFunctionSelectors; + } + + auto compilerOutputOpt = evmoneUtil.compileContract(); + solAssert( + compilerOutputOpt.has_value(), + "Contract could not be optimised via old code gen." + ); + + auto deployResultOpt = evmoneUtil.deployContract(compilerOutputOpt->byteCode); + solAssert( + deployResultOpt.status_code == EVMC_SUCCESS, + "Contract optimised via old code gen could not be deployed." + ); + + // Add address literal of contract compiled via Yul IR. + addressSelector[EVMHost::convertFromEVMC(deployResultOpt.create_address)] = functionSelectors; + + string encodedDataIR = abiEncoding(r.value(), addressSelector, + compilerOutput->methodIdentifiersInContract); + + auto callResultOpt = evmoneUtil.executeContract( + solidity::util::fromHex(encodedDataIR), + deployResultOpt.create_address + ); + solAssert( + callResultOpt.status_code == EVMC_SUCCESS, + "Old code gen contract call failed." + ); + + solidity::bytes resultOpt; + for (size_t i = 0; i < callResultOpt.output_size; i++) + resultOpt.push_back(callResultOpt.output_data[i]); + + if (result != resultOpt) + { + cout << solidity::util::toHex(result) << endl; + cout << solidity::util::toHex(resultOpt) << endl; + } + solAssert( + result == resultOpt, + "Old and new code gen call results do not match." + ); + + EVMHostPrinter pOpt(hostContext, deployResultOpt.create_address); + ostringstream newCodeGen; + newCodeGen << pOpt.state(); + + if (oldCodeGen.str() != newCodeGen.str()) + { + cout << oldCodeGen.str() << endl; + cout << newCodeGen.str() << endl; + } + solAssert( + oldCodeGen.str() == newCodeGen.str(), + "Old and new code gen state do not match." + ); + return 0; + } + catch (runtime_error const&) + { + cout << "Runtime error!" << endl; + return 0; + } + catch (solidity::langutil::UnimplementedFeatureError const&) + { + cout << "Unimplemented feature!" << endl; + return 0; + } + catch (solidity::langutil::CompilerError const& _e) + { + cout << "Compiler error!" << endl; + cout << _e.what() << endl; + return 0; + } + catch (solidity::yul::StackTooDeepError const&) + { + cout << "Stack too deep" << endl; + return 0; + } +}