mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12276 from ethereum/fuzz-RSE
Yul interpreter: Changes required before merging Redundant store eliminator
This commit is contained in:
commit
679f73c1e0
@ -108,13 +108,18 @@ string EwasmTranslationTest::interpret()
|
|||||||
state.maxExprNesting = 64;
|
state.maxExprNesting = 64;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Interpreter::run(state, WasmDialect{}, *m_object->code);
|
Interpreter::run(
|
||||||
|
state,
|
||||||
|
WasmDialect{},
|
||||||
|
*m_object->code,
|
||||||
|
/*disableMemoryTracing=*/false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (InterpreterTerminatedGeneric const&)
|
catch (InterpreterTerminatedGeneric const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stringstream result;
|
stringstream result;
|
||||||
state.dumpTraceAndState(result);
|
state.dumpTraceAndState(result, false);
|
||||||
return result.str();
|
return result.str();
|
||||||
}
|
}
|
||||||
|
@ -94,13 +94,18 @@ string YulInterpreterTest::interpret()
|
|||||||
state.maxExprNesting = 64;
|
state.maxExprNesting = 64;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast);
|
Interpreter::run(
|
||||||
|
state,
|
||||||
|
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}),
|
||||||
|
*m_ast,
|
||||||
|
/*disableMemoryTracing=*/false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (InterpreterTerminatedGeneric const&)
|
catch (InterpreterTerminatedGeneric const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stringstream result;
|
stringstream result;
|
||||||
state.dumpTraceAndState(result);
|
state.dumpTraceAndState(result, false);
|
||||||
return result.str();
|
return result.str();
|
||||||
}
|
}
|
||||||
|
@ -81,10 +81,15 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
|
|||||||
|
|
||||||
ostringstream os1;
|
ostringstream os1;
|
||||||
ostringstream os2;
|
ostringstream os2;
|
||||||
|
// Disable memory tracing to avoid false positive reports
|
||||||
|
// such as unused write to memory e.g.,
|
||||||
|
// { mstore(0, 1) }
|
||||||
|
// that would be removed by the redundant store eliminator.
|
||||||
yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret(
|
yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret(
|
||||||
os1,
|
os1,
|
||||||
stack.parserResult()->code,
|
stack.parserResult()->code,
|
||||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion())
|
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()),
|
||||||
|
/*disableMemoryTracing=*/true
|
||||||
);
|
);
|
||||||
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
||||||
return 0;
|
return 0;
|
||||||
@ -93,7 +98,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
|
|||||||
termReason = yulFuzzerUtil::interpret(
|
termReason = yulFuzzerUtil::interpret(
|
||||||
os2,
|
os2,
|
||||||
stack.parserResult()->code,
|
stack.parserResult()->code,
|
||||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion())
|
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()),
|
||||||
|
/*disableMemoryTracing=*/true
|
||||||
);
|
);
|
||||||
|
|
||||||
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
||||||
|
@ -26,6 +26,7 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
|
|||||||
ostream& _os,
|
ostream& _os,
|
||||||
shared_ptr<yul::Block> _ast,
|
shared_ptr<yul::Block> _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
bool _disableMemoryTracing,
|
||||||
bool _outputStorageOnly,
|
bool _outputStorageOnly,
|
||||||
size_t _maxSteps,
|
size_t _maxSteps,
|
||||||
size_t _maxTraceSize,
|
size_t _maxTraceSize,
|
||||||
@ -52,7 +53,7 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
|
|||||||
TerminationReason reason = TerminationReason::None;
|
TerminationReason reason = TerminationReason::None;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Interpreter::run(state, _dialect, *_ast);
|
Interpreter::run(state, _dialect, *_ast, _disableMemoryTracing);
|
||||||
}
|
}
|
||||||
catch (StepLimitReached const&)
|
catch (StepLimitReached const&)
|
||||||
{
|
{
|
||||||
@ -74,7 +75,7 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
|
|||||||
if (_outputStorageOnly)
|
if (_outputStorageOnly)
|
||||||
state.dumpStorage(_os);
|
state.dumpStorage(_os);
|
||||||
else
|
else
|
||||||
state.dumpTraceAndState(_os);
|
state.dumpTraceAndState(_os, _disableMemoryTracing);
|
||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,10 +32,17 @@ struct yulFuzzerUtil
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Interprets the Yul AST pointed to by @param _ast. Flag @param _outputStorageOnly
|
||||||
|
/// (unset by default) outputs an execution trace of both memory and storage;
|
||||||
|
/// if set, only storage contents are output as part of the execution trace. The
|
||||||
|
/// latter avoids false positives that will be produced by the fuzzer when certain
|
||||||
|
/// optimizer steps are activated e.g., Redundant store eliminator, Equal store
|
||||||
|
/// eliminator.
|
||||||
static TerminationReason interpret(
|
static TerminationReason interpret(
|
||||||
std::ostream& _os,
|
std::ostream& _os,
|
||||||
std::shared_ptr<yul::Block> _ast,
|
std::shared_ptr<yul::Block> _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
bool _disableMemoryTracing = false,
|
||||||
bool _outputStorageOnly = false,
|
bool _outputStorageOnly = false,
|
||||||
size_t _maxSteps = maxSteps,
|
size_t _maxSteps = maxSteps,
|
||||||
size_t _maxTraceSize = maxTraceSize,
|
size_t _maxTraceSize = maxTraceSize,
|
||||||
|
@ -88,10 +88,15 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
|
|
||||||
ostringstream os1;
|
ostringstream os1;
|
||||||
ostringstream os2;
|
ostringstream os2;
|
||||||
|
// Disable memory tracing to avoid false positive reports
|
||||||
|
// such as unused write to memory e.g.,
|
||||||
|
// { mstore(0, 1) }
|
||||||
|
// that would be removed by the redundant store eliminator.
|
||||||
yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret(
|
yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret(
|
||||||
os1,
|
os1,
|
||||||
stack.parserResult()->code,
|
stack.parserResult()->code,
|
||||||
EVMDialect::strictAssemblyForEVMObjects(version)
|
EVMDialect::strictAssemblyForEVMObjects(version),
|
||||||
|
/*disableMemoryTracing=*/true
|
||||||
);
|
);
|
||||||
|
|
||||||
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
||||||
@ -107,12 +112,18 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
termReason = yulFuzzerUtil::interpret(
|
termReason = yulFuzzerUtil::interpret(
|
||||||
os2,
|
os2,
|
||||||
astBlock,
|
astBlock,
|
||||||
EVMDialect::strictAssemblyForEVMObjects(version)
|
EVMDialect::strictAssemblyForEVMObjects(version),
|
||||||
|
true
|
||||||
);
|
);
|
||||||
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
if (yulFuzzerUtil::resourceLimitsExceeded(termReason))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool isTraceEq = (os1.str() == os2.str());
|
bool isTraceEq = (os1.str() == os2.str());
|
||||||
yulAssert(isTraceEq, "Interpreted traces for optimized and unoptimized code differ.");
|
if (!isTraceEq)
|
||||||
|
{
|
||||||
|
cout << os1.str() << endl;
|
||||||
|
cout << os2.str() << endl;
|
||||||
|
yulAssert(false, "Interpreted traces for optimized and unoptimized code differ.");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
#include <libsolutil/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
#include <libsolutil/Numeric.h>
|
#include <libsolutil/Numeric.h>
|
||||||
@ -35,6 +36,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::yul::test;
|
using namespace solidity::yul::test;
|
||||||
|
|
||||||
@ -99,6 +101,7 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
switch (_instruction)
|
switch (_instruction)
|
||||||
{
|
{
|
||||||
case Instruction::STOP:
|
case Instruction::STOP:
|
||||||
|
logTrace(_instruction);
|
||||||
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
||||||
// --------------- arithmetic ---------------
|
// --------------- arithmetic ---------------
|
||||||
case Instruction::ADD:
|
case Instruction::ADD:
|
||||||
@ -204,6 +207,7 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
case Instruction::CALLDATASIZE:
|
case Instruction::CALLDATASIZE:
|
||||||
return m_state.calldata.size();
|
return m_state.calldata.size();
|
||||||
case Instruction::CALLDATACOPY:
|
case Instruction::CALLDATACOPY:
|
||||||
|
logTrace(_instruction, arg);
|
||||||
if (accessMemory(arg[0], arg[2]))
|
if (accessMemory(arg[0], arg[2]))
|
||||||
copyZeroExtended(
|
copyZeroExtended(
|
||||||
m_state.memory, m_state.calldata,
|
m_state.memory, m_state.calldata,
|
||||||
@ -213,6 +217,7 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
case Instruction::CODESIZE:
|
case Instruction::CODESIZE:
|
||||||
return m_state.code.size();
|
return m_state.code.size();
|
||||||
case Instruction::CODECOPY:
|
case Instruction::CODECOPY:
|
||||||
|
logTrace(_instruction, arg);
|
||||||
if (accessMemory(arg[0], arg[2]))
|
if (accessMemory(arg[0], arg[2]))
|
||||||
copyZeroExtended(
|
copyZeroExtended(
|
||||||
m_state.memory, m_state.code,
|
m_state.memory, m_state.code,
|
||||||
@ -339,12 +344,18 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
case Instruction::REVERT:
|
case Instruction::REVERT:
|
||||||
accessMemory(arg[0], arg[1]);
|
accessMemory(arg[0], arg[1]);
|
||||||
logTrace(_instruction, arg);
|
logTrace(_instruction, arg);
|
||||||
|
m_state.storage.clear();
|
||||||
|
m_state.trace.clear();
|
||||||
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
||||||
case Instruction::INVALID:
|
case Instruction::INVALID:
|
||||||
logTrace(_instruction);
|
logTrace(_instruction);
|
||||||
|
m_state.storage.clear();
|
||||||
|
m_state.trace.clear();
|
||||||
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
||||||
case Instruction::SELFDESTRUCT:
|
case Instruction::SELFDESTRUCT:
|
||||||
logTrace(_instruction, arg);
|
logTrace(_instruction, arg);
|
||||||
|
m_state.storage.clear();
|
||||||
|
m_state.trace.clear();
|
||||||
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
|
||||||
case Instruction::POP:
|
case Instruction::POP:
|
||||||
break;
|
break;
|
||||||
@ -507,13 +518,29 @@ void EVMInstructionInterpreter::writeMemoryWord(u256 const& _offset, u256 const&
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EVMInstructionInterpreter::logTrace(evmasm::Instruction _instruction, std::vector<u256> const& _arguments, bytes const& _data)
|
void EVMInstructionInterpreter::logTrace(
|
||||||
|
evmasm::Instruction _instruction,
|
||||||
|
std::vector<u256> const& _arguments,
|
||||||
|
bytes const& _data
|
||||||
|
)
|
||||||
{
|
{
|
||||||
logTrace(evmasm::instructionInfo(_instruction).name, _arguments, _data);
|
logTrace(
|
||||||
|
evmasm::instructionInfo(_instruction).name,
|
||||||
|
SemanticInformation::memory(_instruction) == SemanticInformation::Effect::Write,
|
||||||
|
_arguments,
|
||||||
|
_data
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EVMInstructionInterpreter::logTrace(std::string const& _pseudoInstruction, std::vector<u256> const& _arguments, bytes const& _data)
|
void EVMInstructionInterpreter::logTrace(
|
||||||
|
std::string const& _pseudoInstruction,
|
||||||
|
bool _writesToMemory,
|
||||||
|
std::vector<u256> const& _arguments,
|
||||||
|
bytes const& _data
|
||||||
|
)
|
||||||
{
|
{
|
||||||
|
if (!(_writesToMemory && memWriteTracingDisabled()))
|
||||||
|
{
|
||||||
string message = _pseudoInstruction + "(";
|
string message = _pseudoInstruction + "(";
|
||||||
for (size_t i = 0; i < _arguments.size(); ++i)
|
for (size_t i = 0; i < _arguments.size(); ++i)
|
||||||
message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]);
|
message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]);
|
||||||
@ -526,4 +553,5 @@ void EVMInstructionInterpreter::logTrace(std::string const& _pseudoInstruction,
|
|||||||
m_state.trace.emplace_back("Trace size limit reached.");
|
m_state.trace.emplace_back("Trace size limit reached.");
|
||||||
BOOST_THROW_EXCEPTION(TraceLimitReached());
|
BOOST_THROW_EXCEPTION(TraceLimitReached());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,9 @@ struct InterpreterState;
|
|||||||
class EVMInstructionInterpreter
|
class EVMInstructionInterpreter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit EVMInstructionInterpreter(InterpreterState& _state):
|
explicit EVMInstructionInterpreter(InterpreterState& _state, bool _disableMemWriteTrace):
|
||||||
m_state(_state)
|
m_state(_state),
|
||||||
|
m_disableMemoryWriteInstructions(_disableMemWriteTrace)
|
||||||
{}
|
{}
|
||||||
/// Evaluate instruction
|
/// Evaluate instruction
|
||||||
u256 eval(evmasm::Instruction _instruction, std::vector<u256> const& _arguments);
|
u256 eval(evmasm::Instruction _instruction, std::vector<u256> const& _arguments);
|
||||||
@ -93,12 +94,29 @@ private:
|
|||||||
/// Does not adjust msize, use @a accessMemory for that
|
/// Does not adjust msize, use @a accessMemory for that
|
||||||
void writeMemoryWord(u256 const& _offset, u256 const& _value);
|
void writeMemoryWord(u256 const& _offset, u256 const& _value);
|
||||||
|
|
||||||
void logTrace(evmasm::Instruction _instruction, std::vector<u256> const& _arguments = {}, bytes const& _data = {});
|
void logTrace(
|
||||||
|
evmasm::Instruction _instruction,
|
||||||
|
std::vector<u256> const& _arguments = {},
|
||||||
|
bytes const& _data = {}
|
||||||
|
);
|
||||||
/// Appends a log to the trace representing an instruction or similar operation by string,
|
/// Appends a log to the trace representing an instruction or similar operation by string,
|
||||||
/// with arguments and auxiliary data (if nonempty).
|
/// with arguments and auxiliary data (if nonempty). Flag @param _writesToMemory indicates
|
||||||
void logTrace(std::string const& _pseudoInstruction, std::vector<u256> const& _arguments = {}, bytes const& _data = {});
|
/// whether the instruction writes to (true) or does not write to (false) memory.
|
||||||
|
void logTrace(
|
||||||
|
std::string const& _pseudoInstruction,
|
||||||
|
bool _writesToMemory,
|
||||||
|
std::vector<u256> const& _arguments = {},
|
||||||
|
bytes const& _data = {}
|
||||||
|
);
|
||||||
|
/// @returns disable trace flag.
|
||||||
|
bool memWriteTracingDisabled()
|
||||||
|
{
|
||||||
|
return m_disableMemoryWriteInstructions;
|
||||||
|
}
|
||||||
|
|
||||||
InterpreterState& m_state;
|
InterpreterState& m_state;
|
||||||
|
/// Flag to disable trace of instructions that write to memory.
|
||||||
|
bool m_disableMemoryWriteInstructions;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // solidity::yul::test
|
} // solidity::yul::test
|
||||||
|
@ -55,11 +55,13 @@ void InterpreterState::dumpStorage(ostream& _out) const
|
|||||||
_out << " " << slot.first.hex() << ": " << slot.second.hex() << endl;
|
_out << " " << slot.first.hex() << ": " << slot.second.hex() << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterState::dumpTraceAndState(ostream& _out) const
|
void InterpreterState::dumpTraceAndState(ostream& _out, bool _disableMemoryTrace) const
|
||||||
{
|
{
|
||||||
_out << "Trace:" << endl;
|
_out << "Trace:" << endl;
|
||||||
for (auto const& line: trace)
|
for (auto const& line: trace)
|
||||||
_out << " " << line << endl;
|
_out << " " << line << endl;
|
||||||
|
if (!_disableMemoryTrace)
|
||||||
|
{
|
||||||
_out << "Memory dump:\n";
|
_out << "Memory dump:\n";
|
||||||
map<u256, u256> words;
|
map<u256, u256> words;
|
||||||
for (auto const& [offset, value]: memory)
|
for (auto const& [offset, value]: memory)
|
||||||
@ -67,14 +69,20 @@ void InterpreterState::dumpTraceAndState(ostream& _out) const
|
|||||||
for (auto const& [offset, value]: words)
|
for (auto const& [offset, value]: words)
|
||||||
if (value != 0)
|
if (value != 0)
|
||||||
_out << " " << std::uppercase << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << endl;
|
_out << " " << std::uppercase << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << endl;
|
||||||
|
}
|
||||||
_out << "Storage dump:" << endl;
|
_out << "Storage dump:" << endl;
|
||||||
dumpStorage(_out);
|
dumpStorage(_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::run(InterpreterState& _state, Dialect const& _dialect, Block const& _ast)
|
void Interpreter::run(
|
||||||
|
InterpreterState& _state,
|
||||||
|
Dialect const& _dialect,
|
||||||
|
Block const& _ast,
|
||||||
|
bool _disableMemoryTrace
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Scope scope;
|
Scope scope;
|
||||||
Interpreter{_state, _dialect, scope}(_ast);
|
Interpreter{_state, _dialect, scope, _disableMemoryTrace}(_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::operator()(ExpressionStatement const& _expressionStatement)
|
void Interpreter::operator()(ExpressionStatement const& _expressionStatement)
|
||||||
@ -209,14 +217,14 @@ void Interpreter::operator()(Block const& _block)
|
|||||||
|
|
||||||
u256 Interpreter::evaluate(Expression const& _expression)
|
u256 Interpreter::evaluate(Expression const& _expression)
|
||||||
{
|
{
|
||||||
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables);
|
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableMemoryTrace);
|
||||||
ev.visit(_expression);
|
ev.visit(_expression);
|
||||||
return ev.value();
|
return ev.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
|
vector<u256> Interpreter::evaluateMulti(Expression const& _expression)
|
||||||
{
|
{
|
||||||
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables);
|
ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableMemoryTrace);
|
||||||
ev.visit(_expression);
|
ev.visit(_expression);
|
||||||
return ev.values();
|
return ev.values();
|
||||||
}
|
}
|
||||||
@ -279,7 +287,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
|
|||||||
{
|
{
|
||||||
if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
|
if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
|
||||||
{
|
{
|
||||||
EVMInstructionInterpreter interpreter(m_state);
|
EVMInstructionInterpreter interpreter(m_state, m_disableMemoryTrace);
|
||||||
setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values()));
|
setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -308,7 +316,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
|
|||||||
variables[fun->returnVariables.at(i).name] = 0;
|
variables[fun->returnVariables.at(i).name] = 0;
|
||||||
|
|
||||||
m_state.controlFlowState = ControlFlowState::Default;
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
Interpreter interpreter(m_state, m_dialect, *scope, std::move(variables));
|
Interpreter interpreter(m_state, m_dialect, *scope, m_disableMemoryTrace, std::move(variables));
|
||||||
interpreter(fun->body);
|
interpreter(fun->body);
|
||||||
m_state.controlFlowState = ControlFlowState::Default;
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
|
|
||||||
|
@ -102,7 +102,10 @@ struct InterpreterState
|
|||||||
ControlFlowState controlFlowState = ControlFlowState::Default;
|
ControlFlowState controlFlowState = ControlFlowState::Default;
|
||||||
|
|
||||||
/// Prints execution trace and non-zero storage to @param _out.
|
/// Prints execution trace and non-zero storage to @param _out.
|
||||||
void dumpTraceAndState(std::ostream& _out) const;
|
/// Flag @param _disableMemoryTrace, if set, does not produce a memory dump. This
|
||||||
|
/// avoids false positives reports by the fuzzer when certain optimizer steps are
|
||||||
|
/// activated e.g., Redundant store eliminator, Equal store eliminator.
|
||||||
|
void dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const;
|
||||||
/// Prints non-zero storage to @param _out.
|
/// Prints non-zero storage to @param _out.
|
||||||
void dumpStorage(std::ostream& _out) const;
|
void dumpStorage(std::ostream& _out) const;
|
||||||
};
|
};
|
||||||
@ -124,18 +127,29 @@ struct Scope
|
|||||||
class Interpreter: public ASTWalker
|
class Interpreter: public ASTWalker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void run(InterpreterState& _state, Dialect const& _dialect, Block const& _ast);
|
/// Executes the Yul interpreter. Flag @param _disableMemoryTracing if set ensures that
|
||||||
|
/// instructions that write to memory do not affect @param _state. This
|
||||||
|
/// avoids false positives reports by the fuzzer when certain optimizer steps are
|
||||||
|
/// activated e.g., Redundant store eliminator, Equal store eliminator.
|
||||||
|
static void run(
|
||||||
|
InterpreterState& _state,
|
||||||
|
Dialect const& _dialect,
|
||||||
|
Block const& _ast,
|
||||||
|
bool _disableMemoryTracing
|
||||||
|
);
|
||||||
|
|
||||||
Interpreter(
|
Interpreter(
|
||||||
InterpreterState& _state,
|
InterpreterState& _state,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
Scope& _scope,
|
Scope& _scope,
|
||||||
|
bool _disableMemoryTracing,
|
||||||
std::map<YulString, u256> _variables = {}
|
std::map<YulString, u256> _variables = {}
|
||||||
):
|
):
|
||||||
m_dialect(_dialect),
|
m_dialect(_dialect),
|
||||||
m_state(_state),
|
m_state(_state),
|
||||||
m_variables(std::move(_variables)),
|
m_variables(std::move(_variables)),
|
||||||
m_scope(&_scope)
|
m_scope(&_scope),
|
||||||
|
m_disableMemoryTrace(_disableMemoryTracing)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +187,7 @@ private:
|
|||||||
/// Values of variables.
|
/// Values of variables.
|
||||||
std::map<YulString, u256> m_variables;
|
std::map<YulString, u256> m_variables;
|
||||||
Scope* m_scope;
|
Scope* m_scope;
|
||||||
|
bool m_disableMemoryTrace;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,12 +200,14 @@ public:
|
|||||||
InterpreterState& _state,
|
InterpreterState& _state,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
Scope& _scope,
|
Scope& _scope,
|
||||||
std::map<YulString, u256> const& _variables
|
std::map<YulString, u256> const& _variables,
|
||||||
|
bool _disableMemoryTrace
|
||||||
):
|
):
|
||||||
m_state(_state),
|
m_state(_state),
|
||||||
m_dialect(_dialect),
|
m_dialect(_dialect),
|
||||||
m_variables(_variables),
|
m_variables(_variables),
|
||||||
m_scope(_scope)
|
m_scope(_scope),
|
||||||
|
m_disableMemoryTrace(_disableMemoryTrace)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void operator()(Literal const&) override;
|
void operator()(Literal const&) override;
|
||||||
@ -226,6 +243,8 @@ private:
|
|||||||
std::vector<u256> m_values;
|
std::vector<u256> m_values;
|
||||||
/// Current expression nesting level
|
/// Current expression nesting level
|
||||||
unsigned m_nestingLevel = 0;
|
unsigned m_nestingLevel = 0;
|
||||||
|
/// Flag to disable memory tracing
|
||||||
|
bool m_disableMemoryTrace;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -87,13 +87,13 @@ void interpret(string const& _source)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
|
Dialect const& dialect(EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}));
|
||||||
Interpreter::run(state, dialect, *ast);
|
Interpreter::run(state, dialect, *ast, /*disableMemoryTracing=*/false);
|
||||||
}
|
}
|
||||||
catch (InterpreterTerminatedGeneric const&)
|
catch (InterpreterTerminatedGeneric const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
state.dumpTraceAndState(cout);
|
state.dumpTraceAndState(cout, /*disableMemoryTracing=*/false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user