From 785bfcda99e3760215d364a25f6f49dbcbe81241 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 6 Apr 2021 21:33:55 +0200 Subject: [PATCH] Add real addresses to encoder --- test/evmc/mocked_host.hpp | 2 +- test/tools/ossfuzz/ValueGenerator.cpp | 18 +- test/tools/ossfuzz/ValueGenerator.h | 13 +- test/tools/ossfuzz/solc_ossfuzz.cpp | 332 ++++++++++++++------------ 4 files changed, 211 insertions(+), 154 deletions(-) diff --git a/test/evmc/mocked_host.hpp b/test/evmc/mocked_host.hpp index c7d99e6ad..c70718d7b 100644 --- a/test/evmc/mocked_host.hpp +++ b/test/evmc/mocked_host.hpp @@ -108,7 +108,7 @@ public: }; /// The set of all accounts in the Host, organized by their addresses. - std::unordered_map accounts; + std::map accounts; /// The EVMC transaction context to be returned by get_tx_context(). evmc_tx_context tx_context = {}; diff --git a/test/tools/ossfuzz/ValueGenerator.cpp b/test/tools/ossfuzz/ValueGenerator.cpp index 6ef532607..61cab8bdc 100644 --- a/test/tools/ossfuzz/ValueGenerator.cpp +++ b/test/tools/ossfuzz/ValueGenerator.cpp @@ -150,6 +150,16 @@ string fixedBytes( } } +std::string ValueGenerator::addressLiteral(bool _hexPrefix) +{ + std::uniform_int_distribution dist(0, m_addresses.size() - 1); + std::string addressLiteral; + if (_hexPrefix) + addressLiteral = "0x"; + addressLiteral += m_addresses[dist(m_rand)].hex(); + return addressLiteral; +} + void ValueGenerator::initialiseType(TypeInfo& _t) { switch (_t.type) @@ -177,10 +187,14 @@ void ValueGenerator::initialiseType(TypeInfo& _t) _t.value += "0x" + fixedBytes(static_cast(_t.fixedByteWidth), m_rand(), true); break; case Type::Address: - _t.value += "0x" + fixedBytes(static_cast(FixedBytesWidth::Bytes20), m_rand(), true); + _t.value = addressLiteral(); break; case Type::Function: - _t.value += "0x" + fixedBytes(static_cast(FixedBytesWidth::Bytes24), m_rand(), true); + _t.value += "0x" + + addressLiteral(false) + + ":" + + "0x" + + fixedBytes(static_cast(FixedBytesWidth::Bytes4), m_rand(), true); break; default: solAssert(false, "Value Generator: Invalid value type."); diff --git a/test/tools/ossfuzz/ValueGenerator.h b/test/tools/ossfuzz/ValueGenerator.h index 87ee84a9b..166fa36f0 100644 --- a/test/tools/ossfuzz/ValueGenerator.h +++ b/test/tools/ossfuzz/ValueGenerator.h @@ -18,6 +18,8 @@ #pragma once +#include + #include #include @@ -132,10 +134,15 @@ public: std::string name; std::string value; }; - explicit ValueGenerator(Json::Value const& _type, unsigned _seed): + explicit ValueGenerator( + Json::Value const& _type, + unsigned _seed, + std::vector _addresses + ): m_rand(_seed), m_type(_type), - m_bernoulli(0.5) + m_bernoulli(0.5), + m_addresses(std::move(_addresses)) {} void boolean() { @@ -173,9 +180,11 @@ public: std::vector& _arrayInfo, TypeInfo& _typeInfo ); + std::string addressLiteral(bool _hexPrefix = true); private: std::ostringstream m_stream; std::minstd_rand m_rand; Json::Value const& m_type; std::bernoulli_distribution m_bernoulli; + std::vector m_addresses; }; diff --git a/test/tools/ossfuzz/solc_ossfuzz.cpp b/test/tools/ossfuzz/solc_ossfuzz.cpp index d48706225..c85019120 100644 --- a/test/tools/ossfuzz/solc_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_ossfuzz.cpp @@ -50,166 +50,200 @@ 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, + vector _addressLiterals, + Json::Value const& _methodIdentifiers +) +{ + string abiTypeString; + string abiValueString; + tie(abiTypeString, abiValueString) = ValueGenerator{ + _functionABI["inputs"], + 0, + _addressLiterals + }.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) { -// 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 { - 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(); + EVMVersion version; + EVMHost hostContext(version, evmone); - map sourceCode; - try + 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, + 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; + } + + vector addressLiterals; + for (auto const& account: hostContext.accounts) + addressLiterals.push_back(EVMHost::convertFromEVMC(account.first)); + + hostContext.reset(); + evmoneUtil.reset(true); + evmoneUtil.optSetting(compilerSetting); + 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; + + // Add deployed contract to list of address literals. + addressLiterals.push_back(EVMHost::convertFromEVMC(deployResult.create_address)); + + string encodedData = abiEncoding(r.value(), addressLiterals, + compilerOutput->methodIdentifiersInContract); + auto callResult = evmoneUtil.executeContract( + solidity::util::fromHex(encodedData), + deployResult.create_address + ); + + if (callResult.status_code != EVMC_SUCCESS) { + cout << "Old 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(); + + compilerSetting.runYulOptimiser = true; + compilerSetting.optimizeStackAllocation = true; + hostContext.reset(); + // Remove contract compiled via old code gen from list of address + // literals. + addressLiterals.pop_back(); + evmoneUtil.reset(true); + evmoneUtil.optSetting(compilerSetting); + evmoneUtil.viaIR(true); + 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 new code gen could not be deployed."); + + // Add address literal of contract compiled via Yul IR. + addressLiterals.push_back(EVMHost::convertFromEVMC(deployResultOpt.create_address)); + string encodedDataIR = abiEncoding(r.value(), addressLiterals, + compilerOutput->methodIdentifiersInContract); + + auto callResultOpt = evmoneUtil.executeContract( + solidity::util::fromHex(encodedDataIR), + deployResultOpt.create_address + ); + solAssert(callResultOpt.status_code == EVMC_SUCCESS, "New 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) { - 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, - 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(_size); - 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 << "Old 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]); - cout << solidity::util::toHex(result) << endl; - - EVMHostPrinter p(hostContext, deployResult.create_address); - ostringstream oldCodeGen; - oldCodeGen << p.state(); - - compilerSetting.runYulOptimiser = true; - compilerSetting.optimizeStackAllocation = true; - hostContext.reset(); - evmoneUtil.reset(true); - evmoneUtil.optSetting(compilerSetting); - evmoneUtil.viaIR(true); - 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 new code gen could not be deployed."); - - auto callResultOpt = evmoneUtil.executeContract( - solidity::util::fromHex(methodSig), - deployResultOpt.create_address - ); - solAssert(callResultOpt.status_code == EVMC_SUCCESS, "New 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]); - cout << solidity::util::toHex(resultOpt) << endl; - solAssert(result == resultOpt, "Old and new code gen call results do not match."); + } + solAssert(result == resultOpt, "Old and new code gen call results do not match."); - EVMHostPrinter pOpt(hostContext, deployResultOpt.create_address); - ostringstream newCodeGen; - newCodeGen << pOpt.state(); + 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; } + 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; } }