Merge pull request #10487 from ethereum/yul-interpreter-expression-nesting-level

Yul interpreter: Introduce expression evaluation maximum nesting depth
This commit is contained in:
Bhargava Shastry 2020-12-07 11:26:14 +01:00 committed by GitHub
commit 83fc7d3f7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 70 additions and 5 deletions

View File

@ -102,6 +102,7 @@ string EwasmTranslationTest::interpret()
InterpreterState state; InterpreterState state;
state.maxTraceSize = 10000; state.maxTraceSize = 10000;
state.maxSteps = 1000000; state.maxSteps = 1000000;
state.maxExprNesting = 64;
try try
{ {
Interpreter::run(state, WasmDialect{}, *m_object->code); Interpreter::run(state, WasmDialect{}, *m_object->code);

View File

@ -89,6 +89,7 @@ string YulInterpreterTest::interpret()
InterpreterState state; InterpreterState state;
state.maxTraceSize = 32; state.maxTraceSize = 32;
state.maxSteps = 512; state.maxSteps = 512;
state.maxExprNesting = 64;
try try
{ {
Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast); Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast);

View File

@ -0,0 +1,14 @@
{
function f(x) -> y
{
// 32 nested additions are computed in
// exactly 66 expression evaluation steps
y := add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,x))))))))))))))))))))))))))))))))
}
mstore(0,f(0))
}
// ----
// Trace:
// Maximum expression nesting level reached.
// Memory dump:
// Storage dump:

View File

@ -0,0 +1,14 @@
{
function f(x) -> y
{
// 31 nested additions are computed in
// exactly 64 expression evaluation steps
y := add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,x)))))))))))))))))))))))))))))))
}
mstore(0,f(0))
}
// ----
// Trace:
// Memory dump:
// 0: 000000000000000000000000000000000000000000000000000000000000001f
// Storage dump:

View File

@ -27,12 +27,14 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
shared_ptr<yul::Block> _ast, shared_ptr<yul::Block> _ast,
Dialect const& _dialect, Dialect const& _dialect,
size_t _maxSteps, size_t _maxSteps,
size_t _maxTraceSize size_t _maxTraceSize,
size_t _maxExprNesting
) )
{ {
InterpreterState state; InterpreterState state;
state.maxTraceSize = _maxTraceSize; state.maxTraceSize = _maxTraceSize;
state.maxSteps = _maxSteps; state.maxSteps = _maxSteps;
state.maxExprNesting = _maxExprNesting;
// Add 64 bytes of pseudo-randomly generated calldata so that // Add 64 bytes of pseudo-randomly generated calldata so that
// calldata opcodes perform non trivial work. // calldata opcodes perform non trivial work.
state.calldata = { state.calldata = {
@ -59,6 +61,10 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
{ {
reason = TerminationReason::TraceLimitReached; reason = TerminationReason::TraceLimitReached;
} }
catch (ExpressionNestingLimitReached const&)
{
reason = TerminationReason::ExpresionNestingLimitReached;
}
catch (ExplicitlyTerminated const&) catch (ExplicitlyTerminated const&)
{ {
reason = TerminationReason::ExplicitlyTerminated; reason = TerminationReason::ExplicitlyTerminated;

View File

@ -28,6 +28,7 @@ struct yulFuzzerUtil
ExplicitlyTerminated, ExplicitlyTerminated,
StepLimitReached, StepLimitReached,
TraceLimitReached, TraceLimitReached,
ExpresionNestingLimitReached,
None None
}; };
@ -36,10 +37,12 @@ struct yulFuzzerUtil
std::shared_ptr<yul::Block> _ast, std::shared_ptr<yul::Block> _ast,
Dialect const& _dialect, Dialect const& _dialect,
size_t _maxSteps = maxSteps, size_t _maxSteps = maxSteps,
size_t _maxTraceSize = maxTraceSize size_t _maxTraceSize = maxTraceSize,
size_t _maxExprNesting = maxExprNesting
); );
static size_t constexpr maxSteps = 100; static size_t constexpr maxSteps = 100;
static size_t constexpr maxTraceSize = 75; static size_t constexpr maxTraceSize = 75;
static size_t constexpr maxExprNesting = 64;
}; };
} }

View File

@ -99,7 +99,8 @@ DEFINE_PROTO_FUZZER(Program const& _input)
if ( if (
termReason == yulFuzzerUtil::TerminationReason::StepLimitReached || termReason == yulFuzzerUtil::TerminationReason::StepLimitReached ||
termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached ||
termReason == yulFuzzerUtil::TerminationReason::ExpresionNestingLimitReached
) )
return; return;
@ -109,10 +110,10 @@ DEFINE_PROTO_FUZZER(Program const& _input)
stack.parserResult()->code, stack.parserResult()->code,
EVMDialect::strictAssemblyForEVMObjects(version) EVMDialect::strictAssemblyForEVMObjects(version)
); );
if ( if (
termReason == yulFuzzerUtil::TerminationReason::StepLimitReached || termReason == yulFuzzerUtil::TerminationReason::StepLimitReached ||
termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached ||
termReason == yulFuzzerUtil::TerminationReason::ExpresionNestingLimitReached
) )
return; return;

View File

@ -247,6 +247,7 @@ void Interpreter::incrementStep()
void ExpressionEvaluator::operator()(Literal const& _literal) void ExpressionEvaluator::operator()(Literal const& _literal)
{ {
incrementStep();
static YulString const trueString("true"); static YulString const trueString("true");
static YulString const falseString("false"); static YulString const falseString("false");
@ -256,6 +257,7 @@ void ExpressionEvaluator::operator()(Literal const& _literal)
void ExpressionEvaluator::operator()(Identifier const& _identifier) void ExpressionEvaluator::operator()(Identifier const& _identifier)
{ {
solAssert(m_variables.count(_identifier.name), ""); solAssert(m_variables.count(_identifier.name), "");
incrementStep();
setValue(m_variables.at(_identifier.name)); setValue(m_variables.at(_identifier.name));
} }
@ -326,6 +328,7 @@ void ExpressionEvaluator::evaluateArgs(
vector<optional<LiteralKind>> const* _literalArguments vector<optional<LiteralKind>> const* _literalArguments
) )
{ {
incrementStep();
vector<u256> values; vector<u256> values;
size_t i = 0; size_t i = 0;
/// Function arguments are evaluated in reverse. /// Function arguments are evaluated in reverse.
@ -341,3 +344,13 @@ void ExpressionEvaluator::evaluateArgs(
m_values = std::move(values); m_values = std::move(values);
std::reverse(m_values.begin(), m_values.end()); std::reverse(m_values.begin(), m_values.end());
} }
void ExpressionEvaluator::incrementStep()
{
m_nestingLevel++;
if (m_state.maxExprNesting > 0 && m_nestingLevel > m_state.maxExprNesting)
{
m_state.trace.emplace_back("Maximum expression nesting level reached.");
throw ExpressionNestingLimitReached();
}
}

View File

@ -55,6 +55,10 @@ class TraceLimitReached: public InterpreterTerminatedGeneric
{ {
}; };
class ExpressionNestingLimitReached: public InterpreterTerminatedGeneric
{
};
enum class ControlFlowState enum class ControlFlowState
{ {
Default, Default,
@ -92,6 +96,7 @@ struct InterpreterState
size_t maxTraceSize = 0; size_t maxTraceSize = 0;
size_t maxSteps = 0; size_t maxSteps = 0;
size_t numSteps = 0; size_t numSteps = 0;
size_t maxExprNesting = 0;
ControlFlowState controlFlowState = ControlFlowState::Default; ControlFlowState controlFlowState = ControlFlowState::Default;
void dumpTraceAndState(std::ostream& _out) const; void dumpTraceAndState(std::ostream& _out) const;
@ -202,6 +207,11 @@ private:
std::vector<std::optional<LiteralKind>> const* _literalArguments std::vector<std::optional<LiteralKind>> const* _literalArguments
); );
/// Increment evaluation count, throwing exception if the
/// nesting level is beyond the upper bound configured in
/// the interpreter state.
void incrementStep();
InterpreterState& m_state; InterpreterState& m_state;
Dialect const& m_dialect; Dialect const& m_dialect;
/// Values of variables. /// Values of variables.
@ -209,6 +219,8 @@ private:
Scope& m_scope; Scope& m_scope;
/// Current value of the expression /// Current value of the expression
std::vector<u256> m_values; std::vector<u256> m_values;
/// Current expression nesting level
unsigned m_nestingLevel = 0;
}; };
} }