From 295aef77a47e80974415efc27df5b37dd83ea270 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Sun, 28 Mar 2021 11:43:53 +0200 Subject: [PATCH] Add bytecode optimizer fuzzer --- .../tools/ossfuzz/BytecodeOptimizerFuzzer.cpp | 213 ++++++++++++++++++ test/tools/ossfuzz/CMakeLists.txt | 13 ++ test/tools/ossfuzz/ValueGenerator.cpp | 10 +- 3 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 test/tools/ossfuzz/BytecodeOptimizerFuzzer.cpp diff --git a/test/tools/ossfuzz/BytecodeOptimizerFuzzer.cpp b/test/tools/ossfuzz/BytecodeOptimizerFuzzer.cpp new file mode 100644 index 000000000..998d49c15 --- /dev/null +++ b/test/tools/ossfuzz/BytecodeOptimizerFuzzer.cpp @@ -0,0 +1,213 @@ +/* + 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; + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) +{ +// if (_size <= 600) + { + 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::none(); + CompilerInput cInput = { + version, + sourceCode, + contractName, + compilerSetting, + {}, + true, + false + }; + EvmoneUtility evmoneUtil( + hostContext, + cInput, + contractName, + libraryName, + methodName + ); + + if (!libraryName.empty()) + { + cout << "Deploying library" << endl; + auto l = evmoneUtil.compileAndDeployLibrary(); + if (!l.has_value()) + return 0; + cout << "Deployed" << endl; + } + + hostContext.reset(); + evmoneUtil.reset(true); + evmoneUtil.optSetting(compilerSetting); + auto compilerOutput = evmoneUtil.compileContract(); + if (!compilerOutput.has_value()) + return 0; + + auto r = evmoneUtil.randomFunction(); + if (!r.has_value()) + return 0; + + auto x = ValueGenerator{r.value()["inputs"], 0}.type(); + bool encodeStatus; + string encodedData; + bool functionWithInputs = x.first != "()"; + auto sig = r.value()["name"].asString() + x.first; + cout << sig << endl; + if (functionWithInputs) + { + abicoder::ABICoder coder(abiCoderHeapSize); + auto s = coder.encode(x.first, x.second); + encodeStatus = s.first; + encodedData = s.second; + solAssert(encodeStatus, "Isabelle coder: failed."); + } + + auto deployResult = evmoneUtil.deployContract(compilerOutput->byteCode); + if (deployResult.status_code != EVMC_SUCCESS) + return 0; + + auto methodSig = compilerOutput->methodIdentifiersInContract[sig].asString(); + if (functionWithInputs) + methodSig += encodedData.substr(2, encodedData.size()); + auto callResult = evmoneUtil.executeContract( + solidity::util::fromHex(methodSig), + deployResult.create_address + ); + + if (callResult.status_code != EVMC_SUCCESS) + { + cout << "Unoptimised 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]); + + cout << solidity::util::toHex(result) << endl; + + EVMHostPrinter p(hostContext, deployResult.create_address); + ostringstream oldCodeGen; + oldCodeGen << p.state(); + + compilerSetting = OptimiserSettings::standard(); + hostContext.reset(); + evmoneUtil.reset(true); + evmoneUtil.optSetting(compilerSetting); + auto compilerOutputOpt = evmoneUtil.compileContract(); + solAssert(compilerOutputOpt.has_value(), "Contract could not be optimised."); + + auto deployResultOpt = evmoneUtil.deployContract(compilerOutputOpt->byteCode); + solAssert(deployResultOpt.status_code == EVMC_SUCCESS, "Contract compiled via old optimiser could not be deployed."); + + auto callResultOpt = evmoneUtil.executeContract( + solidity::util::fromHex(methodSig), + deployResultOpt.create_address + ); + solAssert(callResultOpt.status_code == EVMC_SUCCESS, "Old code gen optimised contract call failed."); + + solidity::bytes resultOpt; + for (size_t i = 0; i < callResultOpt.output_size; i++) + resultOpt.push_back(callResultOpt.output_data[i]); + + cout << solidity::util::toHex(resultOpt) << endl; + solAssert(result == resultOpt, "Unoptimised and optimised call results do not match."); + + EVMHostPrinter pOpt(hostContext, deployResultOpt.create_address); + ostringstream newCodeGen; + newCodeGen << pOpt.state(); + + 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; + } + } +} diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 5babc5198..5840741db 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -1,6 +1,7 @@ add_custom_target(ossfuzz) add_dependencies(ossfuzz solc_ossfuzz + bytecode_optimizer_ossfuzz solc_mutator_ossfuzz const_opt_ossfuzz strictasm_diff_ossfuzz @@ -35,6 +36,18 @@ 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}) + add_executable(bytecode_optimizer_ossfuzz + BytecodeOptimizerFuzzer.cpp + ValueGenerator.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + ../../EVMHost.cpp + SolidityEvmoneInterface.cpp + ../../libsolidity/util/ContractABIUtils.cpp + ) + target_link_libraries(bytecode_optimizer_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone abicoder gmp.a) + set_target_properties(bytecode_optimizer_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + add_executable(solc_mutator_ossfuzz solc_ossfuzz.cpp ../fuzzer_common.cpp diff --git a/test/tools/ossfuzz/ValueGenerator.cpp b/test/tools/ossfuzz/ValueGenerator.cpp index a3069b4af..6ef532607 100644 --- a/test/tools/ossfuzz/ValueGenerator.cpp +++ b/test/tools/ossfuzz/ValueGenerator.cpp @@ -219,15 +219,17 @@ void ValueGenerator::initialiseArray( TypeInfo& _typeInfo ) { +#if 0 cout << "Init 1D array" << endl; cout << _typeInfo.value << endl; +#endif _typeInfo.value += "["; std::string separator; for (size_t j = 0; j < _arrayInfo.numElements; j++) { _typeInfo.value += separator; if (_typeInfo.type == Type::Tuple) { - cout << "Tuple inside array" << endl; +// cout << "Tuple inside array" << endl; initialiseTuple(_typeInfo); } else @@ -236,7 +238,9 @@ void ValueGenerator::initialiseArray( separator = ","; } _typeInfo.value += "]"; +#if 0 cout << _typeInfo.value << endl; +#endif } void ValueGenerator::initialiseArray( @@ -389,7 +393,7 @@ void ValueGenerator::typeHelper(Json::Value const& _type, TypeInfo& _typeInfo) if (_typeInfo.arrayInfo.empty()) { if (_typeInfo.type == Type::Tuple) { - cout << "Init tuple" << endl; +// cout << "Init tuple" << endl; initialiseTuple(_typeInfo); } else @@ -397,7 +401,7 @@ void ValueGenerator::typeHelper(Json::Value const& _type, TypeInfo& _typeInfo) } else { - cout << "Init array" << endl; +// cout << "Init array" << endl; initialiseArray(_typeInfo.arrayInfo, _typeInfo); } }