From bcce31b548870c323747085676f118b5b46ed234 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 14 Aug 2017 18:58:56 +0200 Subject: Tests for recursion exploit in parser. --- test/libsolidity/SolidityParser.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 75cad8d9..23276fcd 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1363,6 +1363,30 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(recursion_depth1) +{ + string text("contract C { bytes"); + for (size_t i = 0; i < 30000; i++) + text += "["; + CHECK_PARSE_ERROR(text.c_str(), "Maximum recursion depth reached during parsing"); +} + +BOOST_AUTO_TEST_CASE(recursion_depth2) +{ + string text("contract C { function f() {"); + for (size_t i = 0; i < 30000; i++) + text += "{"; + CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); +} + +BOOST_AUTO_TEST_CASE(recursion_depth3) +{ + string text("contract C { function f() { uint x = f("); + for (size_t i = 0; i < 30000; i++) + text += "("; + CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); +} + BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) { char const* text = R"( -- cgit From 32e43477c336c9180eedd6ad968595e33ecde704 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 14 Aug 2017 18:59:17 +0200 Subject: Prevent too deep recursion in parser. --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 75 +++++++++++++++++++++++++++++++++++++ libsolidity/parsing/Parser.h | 7 ++++ test/libsolidity/SolidityParser.cpp | 12 ++++++ 4 files changed, 95 insertions(+) diff --git a/Changelog.md b/Changelog.md index 4f5615a9..256198f9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: Bugfixes: * Parser: Enforce commas between array and tuple elements. + * Parser: Limit maximum recursion depth. ### 0.4.15 (2017-08-08) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 32bd0966..d3a6aa45 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -64,10 +64,30 @@ private: SourceLocation m_location; }; +/// Utility class that creates an error and throws an exception if the +/// recursion depth is too deep. +class Parser::RecursionGuard +{ +public: + RecursionGuard(Parser& _parser): + m_parser(_parser) + { + m_parser.increaseRecursionDepth(); + } + ~RecursionGuard() + { + m_parser.decreaseRecursionDepth(); + } + +private: + Parser& m_parser; +}; + ASTPointer Parser::parse(shared_ptr const& _scanner) { try { + m_recursionDepth = 0; m_scanner = _scanner; ASTNodeFactory nodeFactory(*this); vector> nodes; @@ -90,6 +110,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) fatalParserError(string("Expected pragma, import directive or contract/interface/library definition.")); } } + solAssert(m_recursionDepth == 0, ""); return nodeFactory.createNode(nodes); } catch (FatalError const&) @@ -102,6 +123,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) ASTPointer Parser::parsePragmaDirective() { + RecursionGuard recursionGuard(*this); // pragma anything* ; // Currently supported: // pragma solidity ^0.4.0 || ^0.3.0; @@ -132,6 +154,7 @@ ASTPointer Parser::parsePragmaDirective() ASTPointer Parser::parseImportDirective() { + RecursionGuard recursionGuard(*this); // import "abc" [as x]; // import * as x from "abc"; // import {a as b, c} from "abc"; @@ -212,6 +235,7 @@ ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token ASTPointer Parser::parseContractDefinition(Token::Value _expectedKind) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") @@ -275,6 +299,7 @@ ASTPointer Parser::parseContractDefinition(Token::Value _exp ASTPointer Parser::parseInheritanceSpecifier() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseUserDefinedTypeName()); vector> arguments; @@ -322,6 +347,7 @@ StateMutability Parser::parseStateMutability(Token::Value _token) Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { + RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; expectToken(Token::Function); if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) @@ -391,6 +417,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") @@ -449,6 +476,7 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A ASTPointer Parser::parseStructDefinition() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Struct); ASTPointer name = expectIdentifierToken(); @@ -466,6 +494,7 @@ ASTPointer Parser::parseStructDefinition() ASTPointer Parser::parseEnumValue() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); return nodeFactory.createNode(expectIdentifierToken()); @@ -473,6 +502,7 @@ ASTPointer Parser::parseEnumValue() ASTPointer Parser::parseEnumDefinition() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Enum); ASTPointer name = expectIdentifierToken(); @@ -501,6 +531,7 @@ ASTPointer Parser::parseVariableDeclaration( ASTPointer const& _lookAheadArrayType ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadArrayType ? ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); ASTPointer type; @@ -593,6 +624,7 @@ ASTPointer Parser::parseVariableDeclaration( ASTPointer Parser::parseModifierDefinition() { + RecursionGuard recursionGuard(*this); ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; }); m_insideModifier = true; @@ -620,6 +652,7 @@ ASTPointer Parser::parseModifierDefinition() ASTPointer Parser::parseEventDefinition() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") @@ -649,6 +682,7 @@ ASTPointer Parser::parseEventDefinition() ASTPointer Parser::parseUsingDirective() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Using); @@ -666,6 +700,7 @@ ASTPointer Parser::parseUsingDirective() ASTPointer Parser::parseModifierInvocation() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseIdentifier()); vector> arguments; @@ -683,6 +718,7 @@ ASTPointer Parser::parseModifierInvocation() ASTPointer Parser::parseIdentifier() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); return nodeFactory.createNode(expectIdentifierToken()); @@ -690,6 +726,7 @@ ASTPointer Parser::parseIdentifier() ASTPointer Parser::parseUserDefinedTypeName() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); vector identifierPath{*expectIdentifierToken()}; @@ -704,6 +741,7 @@ ASTPointer Parser::parseUserDefinedTypeName() ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory) { + RecursionGuard recursionGuard(*this); while (m_scanner->currentToken() == Token::LBrack) { m_scanner->next(); @@ -719,6 +757,7 @@ ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTN ASTPointer Parser::parseTypeName(bool _allowVar) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer type; Token::Value token = m_scanner->currentToken(); @@ -754,6 +793,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) ASTPointer Parser::parseFunctionType() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); FunctionHeaderParserResult header = parseFunctionHeader(true, false); return nodeFactory.createNode( @@ -766,6 +806,7 @@ ASTPointer Parser::parseFunctionType() ASTPointer Parser::parseMapping() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Mapping); expectToken(Token::LParen); @@ -792,6 +833,7 @@ ASTPointer Parser::parseParameterList( bool _allowEmpty ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); vector> parameters; VarDeclParserOptions options(_options); @@ -813,6 +855,7 @@ ASTPointer Parser::parseParameterList( ASTPointer Parser::parseBlock(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::LBrace); vector> statements; @@ -825,6 +868,7 @@ ASTPointer Parser::parseBlock(ASTPointer const& _docString) ASTPointer Parser::parseStatement() { + RecursionGuard recursionGuard(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared(m_scanner->currentCommentLiteral()); @@ -887,6 +931,7 @@ ASTPointer Parser::parseStatement() ASTPointer Parser::parseInlineAssembly(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Assembly); if (m_scanner->currentToken() == Token::StringLiteral) @@ -904,6 +949,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con ASTPointer Parser::parseIfStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::If); expectToken(Token::LParen); @@ -924,6 +970,7 @@ ASTPointer Parser::parseIfStatement(ASTPointer const& _d ASTPointer Parser::parseWhileStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::While); expectToken(Token::LParen); @@ -936,6 +983,7 @@ ASTPointer Parser::parseWhileStatement(ASTPointer con ASTPointer Parser::parseDoWhileStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Do); ASTPointer body = parseStatement(); @@ -951,6 +999,7 @@ ASTPointer Parser::parseDoWhileStatement(ASTPointer c ASTPointer Parser::parseForStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer initExpression; ASTPointer conditionExpression; @@ -984,6 +1033,7 @@ ASTPointer Parser::parseForStatement(ASTPointer const& ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { + RecursionGuard recursionGuard(*this); // These two cases are very hard to distinguish: // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9; // In the first case, x is a type name, in the second it is the name of a variable. @@ -1046,6 +1096,7 @@ ASTPointer Parser::parseVariableDeclarationStateme ASTPointer const& _lookAheadArrayType ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); if (_lookAheadArrayType) nodeFactory.setLocation(_lookAheadArrayType->location()); @@ -1109,6 +1160,7 @@ ASTPointer Parser::parseExpressionStatement( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTPointer expression = parseExpression(_lookAheadIndexAccessStructure); return ASTNodeFactory(*this, expression).createNode(_docString, expression); } @@ -1117,6 +1169,7 @@ ASTPointer Parser::parseExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTPointer expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure); if (Token::isAssignmentOp(m_scanner->currentToken())) { @@ -1145,6 +1198,7 @@ ASTPointer Parser::parseBinaryExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTPointer expression = parseUnaryExpression(_lookAheadIndexAccessStructure); ASTNodeFactory nodeFactory(*this, expression); int precedence = Token::precedence(m_scanner->currentToken()); @@ -1164,6 +1218,7 @@ ASTPointer Parser::parseUnaryExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); Token::Value token = m_scanner->currentToken(); @@ -1192,6 +1247,7 @@ ASTPointer Parser::parseLeftHandSideExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); @@ -1249,6 +1305,7 @@ ASTPointer Parser::parseLeftHandSideExpression( ASTPointer Parser::parsePrimaryExpression() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->currentToken(); ASTPointer expression; @@ -1339,6 +1396,7 @@ ASTPointer Parser::parsePrimaryExpression() vector> Parser::parseFunctionCallListArguments() { + RecursionGuard recursionGuard(*this); vector> arguments; if (m_scanner->currentToken() != Token::RParen) { @@ -1354,6 +1412,7 @@ vector> Parser::parseFunctionCallListArguments() pair>, vector>> Parser::parseFunctionCallArguments() { + RecursionGuard recursionGuard(*this); pair>, vector>> ret; Token::Value token = m_scanner->currentToken(); if (token == Token::LBrace) @@ -1420,6 +1479,7 @@ ASTPointer Parser::typeNameIndexAccessStructure( ) { solAssert(!_path.empty(), ""); + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); SourceLocation location = _path.front()->location(); location.end = _path.back()->location().end; @@ -1452,6 +1512,7 @@ ASTPointer Parser::expressionFromIndexAccessStructure( ) { solAssert(!_path.empty(), ""); + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this, _path.front()); ASTPointer expression(_path.front()); for (size_t i = 1; i < _path.size(); ++i) @@ -1475,11 +1536,25 @@ ASTPointer Parser::expressionFromIndexAccessStructure( ASTPointer Parser::createEmptyParameterList() { + RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); return nodeFactory.createNode(vector>()); } +void Parser::increaseRecursionDepth() +{ + m_recursionDepth++; + if (m_recursionDepth >= 4096) + fatalParserError("Maximum recursion depth reached during parsing."); +} + +void Parser::decreaseRecursionDepth() +{ + solAssert(m_recursionDepth > 0, ""); + m_recursionDepth--; +} + string Parser::currentTokenName() { Token::Value token = m_scanner->currentToken(); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 97e60baa..5e6f3ef6 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -41,6 +41,7 @@ public: private: class ASTNodeFactory; + class RecursionGuard; struct VarDeclParserOptions { @@ -164,8 +165,14 @@ private: /// Creates an empty ParameterList at the current location (used if parameters can be omitted). ASTPointer createEmptyParameterList(); + /// Increases the recursion depth and throws an exception if it is too deep. + void increaseRecursionDepth(); + void decreaseRecursionDepth(); + /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier. bool m_insideModifier = false; + /// Current recursion depth during parsing. + size_t m_recursionDepth = 0; }; } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 23276fcd..30dc80d9 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1387,6 +1387,18 @@ BOOST_AUTO_TEST_CASE(recursion_depth3) CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); } +BOOST_AUTO_TEST_CASE(recursion_depth4) +{ + string text("contract C { function f() { uint a;"); + for (size_t i = 0; i < 30000; i++) + text += "("; + text += "a"; + for (size_t i = 0; i < 30000; i++) + text += "++)"; + text += "}}"; + CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); +} + BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) { char const* text = R"( -- cgit