diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 8919f7cfd..54d3f5109 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -142,6 +142,8 @@ void EVMHost::reset() m_currentAddress = {}; // Clear self destruct records recorded_selfdestructs.clear(); + // Clear call records + recorded_calls.clear(); // Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161). // NOTE: keep this in sync with `EVMHost::call` below. @@ -167,11 +169,21 @@ void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _ben accounts.erase(_addr); accounts[_beneficiary].balance = balance; // Record self destructs - recorded_selfdestructs.push_back({_addr, _beneficiary}); + recorded_selfdestructs.push_back({_addr, _beneficiary, balance}); +} + +void EVMHost::recordCalls(evmc_message const& _message) noexcept +{ + if (recorded_calls.empty()) + recorded_calls.reserve(max_recorded_calls); + + if (recorded_calls.size() < max_recorded_calls) + recorded_calls.emplace_back(_message); } evmc::result EVMHost::call(evmc_message const& _message) noexcept { + recordCalls(_message); if (_message.destination == 0x0000000000000000000000000000000000000001_address) return precompileECRecover(_message); else if (_message.destination == 0x0000000000000000000000000000000000000002_address) @@ -761,17 +773,6 @@ evmc::result EVMHost::resultWithGas( return result; } -void EVMHost::print_all_storage(ostringstream& _os) -{ - for (auto const& [addr, mockedAccount]: accounts) - { - _os << "Address: " << convertFromEVMC(addr) << endl; - for (auto const& [slot, value]: get_address_storage(addr)) - if (get_storage(addr, slot)) - _os << convertFromEVMC(slot) << ": " << convertFromEVMC(value.value) << endl; - } -} - void EVMHost::print_storage_at(evmc::address const& _addr, ostringstream& _os) { for (auto const& [slot, value]: get_address_storage(_addr)) @@ -784,3 +785,65 @@ StorageMap const& EVMHost::get_address_storage(evmc::address const& _addr) assertThrow(account_exists(_addr), Exception, "Account does not exist."); return accounts[_addr].storage; } + +void EVMHost::print_call_records(std::ostringstream& _os) const noexcept +{ + auto callKind = [](evmc_call_kind _kind) -> string + { + switch (_kind) + { + case evmc_call_kind::EVMC_CALL: + return "CALL"; + case evmc_call_kind::EVMC_DELEGATECALL: + return "DELEGATECALL"; + case evmc_call_kind::EVMC_CALLCODE: + return "CALLCODE"; + case evmc_call_kind::EVMC_CREATE: + return "CREATE"; + case evmc_call_kind::EVMC_CREATE2: + return "CREATE2"; + default: + assertThrow(false, Exception, "Invalid call kind."); + } + }; + + for (auto const& record: recorded_calls) + _os << callKind(record.kind) + << " VALUE " + << convertFromEVMC(record.value) + << endl; +} + +void EVMHost::print_selfdestruct_records(ostringstream& _os) const noexcept +{ + for (auto const& record: recorded_selfdestructs) + _os << "SELFDESTRUCT" + << " BENEFICIARY " + << convertFromEVMC(record.beneficiary) + << " BALANCE " + << convertFromEVMC(record.balance) + << endl; +} + +void EVMHost::print_balance(evmc::address const& _addr, ostringstream& _os) const noexcept +{ + _os << "BALANCE " << convertFromEVMC(get_balance(_addr)) << endl; +} + +string EVMHost::dumpState(evmc::address _addr) +{ + ostringstream stateStream; + + // Print state and execution trace. + if (account_exists(_addr)) + { + print_storage_at(_addr, stateStream); + print_balance(_addr, stateStream); + } + else + print_selfdestruct_records(stateStream); + + print_call_records(stateStream); + + return stateStream.str(); +} diff --git a/test/EVMHost.h b/test/EVMHost.h index cba7256b9..b6b162da7 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -61,11 +61,6 @@ public: tx_context.block_timestamp += 15; recorded_logs.clear(); } - /// Prints contents of storage at all addresses in host to @param _os. - void print_all_storage(std::ostringstream& _os); - - /// Prints contents of storage at @param _addr to @param _os. - void print_storage_at(evmc::address const& _addr, std::ostringstream& _os); /// @returns contents of storage at @param _addr. std::unordered_map const& get_address_storage(evmc::address const& _addr); @@ -92,9 +87,24 @@ public: return m_vm.has_capability(capability); } + /// @returns the state as a string. State includes storage at and balance + /// of account at @param _addr and execution trace of the host post reset. + std::string dumpState(evmc::address _addr); + private: evmc::address m_currentAddress = {}; + /// Records calls made via @param _message. + void recordCalls(evmc_message const& _message) noexcept; + /// Prints contents of storage at @param _addr to @param _os. + void print_storage_at(evmc::address const& _addr, std::ostringstream& _os); + /// Prints call summary to @param _os. + void print_call_records(std::ostringstream& _os) const noexcept; + /// Print self destruct records to @param _os. + void print_selfdestruct_records(std::ostringstream& _os) const noexcept; + /// Print balance of @param _addr to @param _os. + void print_balance(evmc::address const& _addr, std::ostringstream& _os) const noexcept; + static evmc::result precompileECRecover(evmc_message const& _message) noexcept; static evmc::result precompileSha256(evmc_message const& _message) noexcept; static evmc::result precompileRipeMD160(evmc_message const& _message) noexcept; diff --git a/test/evmc/mocked_host.hpp b/test/evmc/mocked_host.hpp index 57f0cb79b..c36bd3a04 100644 --- a/test/evmc/mocked_host.hpp +++ b/test/evmc/mocked_host.hpp @@ -91,10 +91,15 @@ public: /// The address of the beneficiary account. address beneficiary; + /// The balance of the self-destructed account. + uint256be balance; + /// Equal operator. bool operator==(const selfdestuct_record& other) const noexcept { - return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary; + return selfdestructed == other.selfdestructed && + beneficiary == other.beneficiary && + balance == other.balance; } }; @@ -265,7 +270,7 @@ public: void selfdestruct(const address& addr, const address& beneficiary) noexcept override { record_account_access(addr); - recorded_selfdestructs.push_back({addr, beneficiary}); + recorded_selfdestructs.push_back({addr, beneficiary, get_balance(addr)}); } /// Call/create other contract (EVMC host method). diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index ff3bf3419..265533397 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -119,11 +119,10 @@ if (OSSFUZZ) target_link_libraries(stack_reuse_codegen_ossfuzz PRIVATE yul evmc evmone-standalone - yulInterpreter protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a - ) + ) set_target_properties(stack_reuse_codegen_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) target_compile_options(stack_reuse_codegen_ossfuzz PUBLIC ${COMPILE_OPTIONS} diff --git a/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp b/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp index 33dbc89b0..05dbc0e60 100644 --- a/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp +++ b/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp @@ -41,7 +41,6 @@ using namespace solidity; using namespace solidity::test; using namespace solidity::test::fuzzer; using namespace solidity::yul; -using namespace solidity::yul::test; using namespace solidity::yul::test::yul_fuzzer; using namespace solidity::langutil; using namespace std; @@ -107,10 +106,8 @@ DEFINE_PROTO_FUZZER(Program const& _input) if (callResult.status_code == EVMC_OUT_OF_GAS) return; - if (YulEvmoneUtility{}.checkSelfDestructs(hostContext, deployResult.create_address)) - return; - ostringstream unoptimizedStorage; - hostContext.print_storage_at(deployResult.create_address, unoptimizedStorage); + ostringstream unoptimizedState; + unoptimizedState << hostContext.dumpState(deployResult.create_address); settings.runYulOptimiser = true; settings.optimizeStackAllocation = true; @@ -123,6 +120,9 @@ DEFINE_PROTO_FUZZER(Program const& _input) { return; } + + // Reset host before running optimised code. + hostContext.reset(); evmc::result deployResultOpt = YulEvmoneUtility{}.deployCode(optimisedByteCode, hostContext); solAssert( deployResultOpt.status_code == EVMC_SUCCESS, @@ -135,12 +135,10 @@ DEFINE_PROTO_FUZZER(Program const& _input) callResultOpt.status_code != EVMC_REVERT, "SolidityEvmoneInterface: EVM One reverted" ); - if (YulEvmoneUtility{}.checkSelfDestructs(hostContext, deployResultOpt.create_address)) - return; - ostringstream optimizedStorage; - hostContext.print_storage_at(deployResultOpt.create_address, optimizedStorage); + ostringstream optimizedState; + optimizedState << hostContext.dumpState(deployResultOpt.create_address); solAssert( - unoptimizedStorage.str() == optimizedStorage.str(), + unoptimizedState.str() == optimizedState.str(), "Storage of unoptimised and optimised stack reused code do not match." ); } diff --git a/test/tools/ossfuzz/YulEvmoneInterface.cpp b/test/tools/ossfuzz/YulEvmoneInterface.cpp index b4af16e8d..437585459 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.cpp +++ b/test/tools/ossfuzz/YulEvmoneInterface.cpp @@ -78,12 +78,3 @@ evmc_message YulEvmoneUtility::callMessage(evmc_address _address) call.kind = EVMC_CALL; return call; } - -bool YulEvmoneUtility::checkSelfDestructs(EVMHost& _host, evmc_address _address) -{ - for (auto const& selfDestructRecord: _host.recorded_selfdestructs) - if (selfDestructRecord.selfdestructed == _address) - return true; - return false; -} - diff --git a/test/tools/ossfuzz/YulEvmoneInterface.h b/test/tools/ossfuzz/YulEvmoneInterface.h index 1e81765fe..780c50275 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.h +++ b/test/tools/ossfuzz/YulEvmoneInterface.h @@ -54,8 +54,5 @@ struct YulEvmoneUtility static evmc::result deployCode(solidity::bytes const& _input, EVMHost& _host); /// @returns call message to be sent to @param _address. static evmc_message callMessage(evmc_address _address); - /// @returns true if @param _address is in the list of self destructed - /// accounts, false otherwise. - static bool checkSelfDestructs(EVMHost& _host, evmc_address _address); }; }