From fcef97043a9ea094595029db4580cc216e44ec58 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Thu, 23 Jul 2020 11:58:10 +0200 Subject: [PATCH] Yul interpreter: Limit nesting level --- test/libyul/EwasmTranslationTest.cpp | 1 + test/libyul/YulInterpreterTest.cpp | 1 + test/tools/ossfuzz/yulFuzzerCommon.cpp | 8 +++++++- test/tools/ossfuzz/yulFuzzerCommon.h | 5 ++++- test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 12 +++++++++--- test/tools/yulInterpreter/Interpreter.cpp | 13 +++++++++++++ test/tools/yulInterpreter/Interpreter.h | 12 ++++++++++++ 7 files changed, 47 insertions(+), 5 deletions(-) diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index be9ed77d9..5b6797d4b 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -100,6 +100,7 @@ string EwasmTranslationTest::interpret() InterpreterState state; state.maxTraceSize = 10000; state.maxSteps = 1000000; + state.maxExprNesting = 60; try { Interpreter::run(state, WasmDialect{}, *m_object->code); diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 4452eb0ca..5d63d3692 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -89,6 +89,7 @@ string YulInterpreterTest::interpret() InterpreterState state; state.maxTraceSize = 10; state.maxSteps = 150; + state.maxExprNesting = 60; try { Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast); diff --git a/test/tools/ossfuzz/yulFuzzerCommon.cpp b/test/tools/ossfuzz/yulFuzzerCommon.cpp index 86cea8327..d977c7761 100644 --- a/test/tools/ossfuzz/yulFuzzerCommon.cpp +++ b/test/tools/ossfuzz/yulFuzzerCommon.cpp @@ -27,12 +27,14 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret( shared_ptr _ast, Dialect const& _dialect, size_t _maxSteps, - size_t _maxTraceSize + size_t _maxTraceSize, + size_t _maxExprNesting ) { InterpreterState state; state.maxTraceSize = _maxTraceSize; state.maxSteps = _maxSteps; + state.maxExprNesting = _maxExprNesting; // Add 64 bytes of pseudo-randomly generated calldata so that // calldata opcodes perform non trivial work. state.calldata = { @@ -59,6 +61,10 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret( { reason = TerminationReason::TraceLimitReached; } + catch (ExpressionNestingLimitReached const&) + { + reason = TerminationReason::ExpresionNestingLimitReached; + } catch (ExplicitlyTerminated const&) { reason = TerminationReason::ExplicitlyTerminated; diff --git a/test/tools/ossfuzz/yulFuzzerCommon.h b/test/tools/ossfuzz/yulFuzzerCommon.h index cc20d6163..a32c75705 100644 --- a/test/tools/ossfuzz/yulFuzzerCommon.h +++ b/test/tools/ossfuzz/yulFuzzerCommon.h @@ -28,6 +28,7 @@ struct yulFuzzerUtil ExplicitlyTerminated, StepLimitReached, TraceLimitReached, + ExpresionNestingLimitReached, None }; @@ -36,10 +37,12 @@ struct yulFuzzerUtil std::shared_ptr _ast, Dialect const& _dialect, 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 maxTraceSize = 75; + static size_t constexpr maxExprNesting = 60; }; } diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index edbcf133a..c6dfa8179 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -91,19 +91,25 @@ DEFINE_PROTO_FUZZER(Program const& _input) ostringstream os1; ostringstream os2; - yulFuzzerUtil::interpret( + yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( os1, stack.parserResult()->code, EVMDialect::strictAssemblyForEVMObjects(version) ); + if ( + termReason == yulFuzzerUtil::TerminationReason::StepLimitReached || + termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached || + termReason == yulFuzzerUtil::TerminationReason::ExpresionNestingLimitReached + ) + return; + stack.optimize(); - yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( + termReason = yulFuzzerUtil::interpret( os2, stack.parserResult()->code, EVMDialect::strictAssemblyForEVMObjects(version) ); - if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) return; diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index a8496793f..d5187e58b 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -247,6 +247,7 @@ void Interpreter::incrementStep() void ExpressionEvaluator::operator()(Literal const& _literal) { + incrementStep(); static YulString const trueString("true"); static YulString const falseString("false"); @@ -256,6 +257,7 @@ void ExpressionEvaluator::operator()(Literal const& _literal) void ExpressionEvaluator::operator()(Identifier const& _identifier) { solAssert(m_variables.count(_identifier.name), ""); + incrementStep(); setValue(m_variables.at(_identifier.name)); } @@ -319,6 +321,7 @@ void ExpressionEvaluator::setValue(u256 _value) void ExpressionEvaluator::evaluateArgs(vector const& _expr) { + incrementStep(); vector values; /// Function arguments are evaluated in reverse. for (auto const& expr: _expr | boost::adaptors::reversed) @@ -329,3 +332,13 @@ void ExpressionEvaluator::evaluateArgs(vector const& _expr) m_values = std::move(values); 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(); + } +} diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 00e02f6e2..7afa13f08 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -55,6 +55,10 @@ class TraceLimitReached: public InterpreterTerminatedGeneric { }; +class ExpressionNestingLimitReached: public InterpreterTerminatedGeneric +{ +}; + enum class ControlFlowState { Default, @@ -92,6 +96,7 @@ struct InterpreterState size_t maxTraceSize = 0; size_t maxSteps = 0; size_t numSteps = 0; + size_t maxExprNesting = 0; ControlFlowState controlFlowState = ControlFlowState::Default; void dumpTraceAndState(std::ostream& _out) const; @@ -199,6 +204,11 @@ private: /// stores it in m_value. void evaluateArgs(std::vector const& _expr); + /// Increment evaluation count, throwing exception if the + /// nesting level is beyond the upper bound configured in + /// the interpreter state. + void incrementStep(); + InterpreterState& m_state; Dialect const& m_dialect; /// Values of variables. @@ -206,6 +216,8 @@ private: Scope& m_scope; /// Current value of the expression std::vector m_values; + /// Current expression nesting level + unsigned m_nestingLevel = 0; }; }