Skip to content

Commit

Permalink
Parser recursion check
Browse files Browse the repository at this point in the history
Let the parser issue a compile-time error when the C stack is about to overflow, instead of crashing the VM.

BUG=24476
R=rmacnak@google.com

Review URL: https://codereview.chromium.org/1620493002 .
  • Loading branch information
mhausner committed Jan 22, 2016
1 parent c60c5a5 commit 783cc8a
Show file tree
Hide file tree
Showing 6 changed files with 10,077 additions and 2 deletions.
43 changes: 41 additions & 2 deletions runtime/vm/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@ class BoolScope : public ValueObject {
};


class RecursionChecker : public ValueObject {
public:
explicit RecursionChecker(Parser* p) : parser_(p) {
parser_->recursion_counter_++;
// No need to check the stack unless the parser is in an unusually deep
// recurive state. Thus, we omit the more expensive stack checks in
// the common case.
const int kMaxUncheckedDepth = 100; // Somewhat arbitrary.
if (parser_->recursion_counter_ > kMaxUncheckedDepth) {
parser_->CheckStack();
}
}
~RecursionChecker() {
parser_->recursion_counter_--;
}

private:
Parser* parser_;
};


static RawTypeArguments* NewTypeArguments(
const GrowableArray<AbstractType*>& objs) {
const TypeArguments& a =
Expand Down Expand Up @@ -349,7 +370,8 @@ Parser::Parser(const Script& script, const Library& library, intptr_t token_pos)
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0) {
trace_indent_(0),
recursion_counter_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!library.IsNull());
}
Expand Down Expand Up @@ -383,7 +405,8 @@ Parser::Parser(const Script& script,
last_used_try_index_(0),
unregister_pending_function_(false),
async_temp_scope_(NULL),
trace_indent_(0) {
trace_indent_(0),
recursion_counter_(0) {
ASSERT(tokens_iterator_.IsValid());
ASSERT(!current_function().IsNull());
EnsureExpressionTemp();
Expand Down Expand Up @@ -6047,6 +6070,18 @@ void Parser::ParseTopLevel() {
}


void Parser::CheckStack() {
volatile uword c_stack_pos = Isolate::GetCurrentStackPointer();
volatile uword c_stack_base = OSThread::Current()->stack_base();
volatile uword c_stack_limit =
c_stack_base - OSThread::GetSpecifiedStackSize();
// Note: during early initialization the stack_base() can return 0.
if ((c_stack_base > 0) && (c_stack_pos < c_stack_limit)) {
ReportError("stack overflow while parsing");
}
}


void Parser::ChainNewBlock(LocalScope* outer_scope) {
Block* block = new(Z) Block(
current_block_,
Expand Down Expand Up @@ -7950,6 +7985,7 @@ void Parser::ParseStatementSequence() {
TRACE_PARSER("ParseStatementSequence");
const bool dead_code_allowed = true;
bool abrupt_completing_seen = false;
RecursionChecker rc(this);
while (CurrentToken() != Token::kRBRACE) {
const intptr_t statement_pos = TokenPos();
AstNode* statement = ParseStatement();
Expand Down Expand Up @@ -7989,6 +8025,7 @@ SequenceNode* Parser::ParseNestedStatement(bool parsing_loop_body,
ParseStatementSequence();
ExpectToken(Token::kRBRACE);
} else {
RecursionChecker rc(this);
AstNode* statement = ParseStatement();
if (statement != NULL) {
current_block_->statements->Add(statement);
Expand Down Expand Up @@ -10740,6 +10777,8 @@ AstNode* Parser::ParseExpr(bool require_compiletime_const,
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL;
const intptr_t expr_pos = TokenPos();

RecursionChecker rc(this);

if (CurrentToken() == Token::kTHROW) {
if (require_compiletime_const) {
ReportError("'throw expr' is not a valid compile-time constant");
Expand Down
7 changes: 7 additions & 0 deletions runtime/vm/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct MemberDesc;
struct ParamList;
struct QualIdent;
class TopLevel;
class RecursionChecker;

// The class ParsedFunction holds the result of parsing a function.
class ParsedFunction : public ZoneAllocated {
Expand Down Expand Up @@ -365,6 +366,9 @@ class Parser : public ValueObject {
const Function& constructor,
const TypeArguments& type_arguments);

// Report error if parsed code is too deeply nested; avoid stack overflow.
void CheckStack();

// Report already formatted error.
static void ReportError(const Error& error);

Expand Down Expand Up @@ -894,6 +898,9 @@ class Parser : public ValueObject {
// Indentation of parser trace.
intptr_t trace_indent_;

intptr_t recursion_counter_;
friend class RecursionChecker;

DISALLOW_COPY_AND_ASSIGN(Parser);
};

Expand Down
11 changes: 11 additions & 0 deletions tests/language/deep_nesting1_test.dart

Large diffs are not rendered by default.

Loading

0 comments on commit 783cc8a

Please sign in to comment.