Merge pull request #2770 from ethereum/recursionInAsm

Also prevent too much recursion in the assembly parser.
This commit is contained in:
chriseth 2017-08-24 11:14:01 +02:00 committed by GitHub
commit 8af6f193bc
8 changed files with 71 additions and 39 deletions

View File

@ -13,6 +13,7 @@ Features:
Bugfixes:
* Assembly Parser: Be more strict about number literals.
* Assembly Parser: Limit maximum recursion depth.
* Parser: Enforce commas between array and tuple elements.
* Parser: Limit maximum recursion depth.
* Type Checker: Crash fix related to ``using``.

View File

@ -36,6 +36,7 @@ using namespace dev::solidity::assembly;
shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
m_recursionDepth = 0;
try
{
m_scanner = _scanner;
@ -51,6 +52,7 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann
assembly::Block Parser::parseBlock()
{
RecursionGuard recursionGuard(*this);
assembly::Block block = createWithLocation<Block>();
expectToken(Token::LBrace);
while (currentToken() != Token::RBrace)
@ -62,6 +64,7 @@ assembly::Block Parser::parseBlock()
assembly::Statement Parser::parseStatement()
{
RecursionGuard recursionGuard(*this);
switch (currentToken())
{
case Token::Let:
@ -158,6 +161,7 @@ assembly::Statement Parser::parseStatement()
assembly::Case Parser::parseCase()
{
RecursionGuard recursionGuard(*this);
assembly::Case _case = createWithLocation<assembly::Case>();
if (m_scanner->currentToken() == Token::Default)
m_scanner->next();
@ -178,6 +182,7 @@ assembly::Case Parser::parseCase()
assembly::ForLoop Parser::parseForLoop()
{
RecursionGuard recursionGuard(*this);
ForLoop forLoop = createWithLocation<ForLoop>();
expectToken(Token::For);
forLoop.pre = parseBlock();
@ -192,6 +197,7 @@ assembly::ForLoop Parser::parseForLoop()
assembly::Statement Parser::parseExpression()
{
RecursionGuard recursionGuard(*this);
Statement operation = parseElementaryOperation(true);
if (operation.type() == typeid(Instruction))
{
@ -254,6 +260,7 @@ std::map<dev::solidity::Instruction, string> const& Parser::instructionNames()
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
RecursionGuard recursionGuard(*this);
Statement ret;
switch (currentToken())
{
@ -342,6 +349,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
RecursionGuard recursionGuard(*this);
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let);
while (true)
@ -366,6 +374,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
RecursionGuard recursionGuard(*this);
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
expectToken(Token::Function);
funDef.name = expectAsmIdentifier();
@ -397,6 +406,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
{
RecursionGuard recursionGuard(*this);
if (_instruction.type() == typeid(Instruction))
{
solAssert(!m_julia, "Instructions are invalid in JULIA");
@ -479,6 +489,7 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
TypedName Parser::parseTypedName()
{
RecursionGuard recursionGuard(*this);
TypedName typedName = createWithLocation<TypedName>();
typedName.name = expectAsmIdentifier();
if (m_julia)

View File

@ -64,25 +64,6 @@ 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:
explicit RecursionGuard(Parser& _parser):
m_parser(_parser)
{
m_parser.increaseRecursionDepth();
}
~RecursionGuard()
{
m_parser.decreaseRecursionDepth();
}
private:
Parser& m_parser;
};
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
try
@ -1543,19 +1524,6 @@ ASTPointer<ParameterList> Parser::createEmptyParameterList()
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
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();

View File

@ -41,7 +41,6 @@ public:
private:
class ASTNodeFactory;
class RecursionGuard;
struct VarDeclParserOptions
{
@ -165,14 +164,8 @@ private:
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
ASTPointer<ParameterList> 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;
};
}

View File

@ -101,6 +101,19 @@ void ParserBase::expectToken(Token::Value _value)
m_scanner->next();
}
void ParserBase::increaseRecursionDepth()
{
m_recursionDepth++;
if (m_recursionDepth >= 3000)
fatalParserError("Maximum recursion depth reached during parsing.");
}
void ParserBase::decreaseRecursionDepth()
{
solAssert(m_recursionDepth > 0, "");
m_recursionDepth--;
}
void ParserBase::parserError(string const& _description)
{
m_errorReporter.parserError(SourceLocation(position(), position(), sourceName()), _description);

View File

@ -41,6 +41,20 @@ public:
std::shared_ptr<std::string const> const& sourceName() const;
protected:
/// Utility class that creates an error and throws an exception if the
/// recursion depth is too deep.
class RecursionGuard
{
public:
explicit RecursionGuard(ParserBase& _parser): m_parser(_parser)
{
m_parser.increaseRecursionDepth();
}
~RecursionGuard() { m_parser.decreaseRecursionDepth(); }
private:
ParserBase& m_parser;
};
/// Start position of the current token
int position() const;
/// End position of the current token
@ -56,6 +70,10 @@ protected:
Token::Value advance();
///@}
/// Increases the recursion depth and throws an exception if it is too deep.
void increaseRecursionDepth();
void decreaseRecursionDepth();
/// Creates a @ref ParserError and annotates it with the current position and the
/// given @a _description.
void parserError(std::string const& _description);
@ -67,6 +85,8 @@ protected:
std::shared_ptr<Scanner> m_scanner;
/// The reference to the list of errors and warning to add errors/warnings during parsing
ErrorReporter& m_errorReporter;
/// Current recursion depth during parsing.
size_t m_recursionDepth = 0;
};
}

View File

@ -237,6 +237,18 @@ BOOST_AUTO_TEST_CASE(builtin_types)
BOOST_CHECK(successParse("{ let x:s256 := 1:s256 }"));
}
BOOST_AUTO_TEST_CASE(recursion_depth)
{
string input;
for (size_t i = 0; i < 20000; i++)
input += "{";
input += "let x:u256 := 0:u256";
for (size_t i = 0; i < 20000; i++)
input += "}";
CHECK_ERROR(input, ParserError, "recursion");
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -400,6 +400,20 @@ BOOST_AUTO_TEST_CASE(instruction_too_many_arguments)
CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (\"mul\" expects 2 arguments)");
}
BOOST_AUTO_TEST_CASE(recursion_depth)
{
string input;
for (size_t i = 0; i < 20000; i++)
input += "{";
input += "let x := 0";
for (size_t i = 0; i < 20000; i++)
input += "}";
CHECK_PARSE_ERROR(input, ParserError, "recursion");
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(Printing)