EVMHost: Add tracing features needed for fuzzing.

This commit is contained in:
Bhargava Shastry 2021-03-05 19:56:43 +01:00
parent 89946b15d2
commit 34c08ea2c6
7 changed files with 106 additions and 43 deletions

View File

@ -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();
}

View File

@ -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<evmc::bytes32, evmc::storage_value> 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;

View File

@ -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).

View File

@ -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}

View File

@ -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."
);
}

View File

@ -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;
}

View File

@ -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);
};
}