diff --git a/src/ast_def_macros.hpp b/src/ast_def_macros.hpp index 78ba1f4ce4..253b05a918 100644 --- a/src/ast_def_macros.hpp +++ b/src/ast_def_macros.hpp @@ -29,6 +29,11 @@ class LocalOption { }; #define LOCAL_FLAG(name,opt) LocalOption flag_##name(name, opt) +#define LOCAL_COUNT(name,opt) LocalOption cnt_##name(name, opt) + +#define NESTING_GUARD(name) \ + LocalOption cnt_##name(name, name + 1); \ + if (nestings > MAX_NESTING) throw Exception::NestingLimitError(pstate); \ #define ATTACH_OPERATIONS()\ virtual void perform(Operation* op) { (*op)(this); }\ diff --git a/src/error_handling.cpp b/src/error_handling.cpp index 7316e6a627..d85f095985 100644 --- a/src/error_handling.cpp +++ b/src/error_handling.cpp @@ -59,6 +59,10 @@ namespace Sass { : Base(pstate, msg, import_stack) { } + NestingLimitError::NestingLimitError(ParserState pstate, std::string msg, std::vector* import_stack) + : Base(pstate, msg, import_stack) + { } + UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op) : lhs(lhs), rhs(rhs), op(op) { diff --git a/src/error_handling.hpp b/src/error_handling.hpp index 9818891003..ec00817ebf 100644 --- a/src/error_handling.hpp +++ b/src/error_handling.hpp @@ -17,6 +17,7 @@ namespace Sass { const std::string def_msg = "Invalid sass detected"; const std::string def_op_msg = "Undefined operation"; const std::string def_op_null_msg = "Invalid null operation"; + const std::string def_nesting_limit = "Code too deeply neested"; class Base : public std::runtime_error { protected: @@ -83,6 +84,12 @@ namespace Sass { virtual ~InvalidSyntax() throw() {}; }; + class NestingLimitError : public Base { + public: + NestingLimitError(ParserState pstate, std::string msg = def_nesting_limit, std::vector* import_stack = 0); + virtual ~NestingLimitError() throw() {}; + }; + /* common virtual base class (has no pstate) */ class OperationError : public std::runtime_error { protected: diff --git a/src/parser.cpp b/src/parser.cpp index 644705f586..4cb4a29bec 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -503,6 +503,7 @@ namespace Sass { // a ruleset connects a selector and a block Ruleset_Obj Parser::parse_ruleset(Lookahead lookahead) { + NESTING_GUARD(nestings); // inherit is_root from parent block Block_Obj parent = block_stack.back(); bool is_root = parent && parent->is_root(); @@ -535,6 +536,7 @@ namespace Sass { // in the eval stage we will be re-parse it into an actual selector Selector_Schema_Obj Parser::parse_selector_schema(const char* end_of_selector, bool chroot) { + NESTING_GUARD(nestings); // move up to the start lex< optional_spaces >(); const char* i = position; @@ -646,6 +648,7 @@ namespace Sass { { bool reloop; bool had_linefeed = false; + NESTING_GUARD(nestings); Complex_Selector_Obj sel; Selector_List_Obj group = SASS_MEMORY_NEW(Selector_List, pstate); group->media_block(last_media_block); @@ -700,6 +703,7 @@ namespace Sass { Complex_Selector_Obj Parser::parse_complex_selector(bool chroot) { + NESTING_GUARD(nestings); String_Obj reference = 0; lex < block_comment >(); advanceToNextToken(); @@ -1055,6 +1059,7 @@ namespace Sass { Expression_Obj Parser::parse_map() { + NESTING_GUARD(nestings); Expression_Obj key = parse_list(); List_Obj map = SASS_MEMORY_NEW(List, pstate, 0, SASS_HASH); @@ -1098,6 +1103,7 @@ namespace Sass { Expression_Obj Parser::parse_bracket_list() { + NESTING_GUARD(nestings); // check if we have an empty list // return the empty list as such if (peek_css< list_terminator >(position)) @@ -1144,12 +1150,14 @@ namespace Sass { // so to speak: we unwrap items from lists if possible here! Expression_Obj Parser::parse_list(bool delayed) { + NESTING_GUARD(nestings); return parse_comma_list(delayed); } // will return singletons unwrapped Expression_Obj Parser::parse_comma_list(bool delayed) { + NESTING_GUARD(nestings); // check if we have an empty list // return the empty list as such if (peek_css< list_terminator >(position)) @@ -1189,6 +1197,7 @@ namespace Sass { // will return singletons unwrapped Expression_Obj Parser::parse_space_list() { + NESTING_GUARD(nestings); Expression_Obj disj1 = parse_disjunction(); // if it's a singleton, return it (don't wrap it) if (peek_css< space_list_terminator >(position) @@ -1213,6 +1222,7 @@ namespace Sass { // parse logical OR operation Expression_Obj Parser::parse_disjunction() { + NESTING_GUARD(nestings); advanceToNextToken(); ParserState state(pstate); // parse the left hand side conjunction @@ -1234,6 +1244,7 @@ namespace Sass { // parse logical AND operation Expression_Obj Parser::parse_conjunction() { + NESTING_GUARD(nestings); advanceToNextToken(); ParserState state(pstate); // parse the left hand side relation @@ -1256,6 +1267,7 @@ namespace Sass { // parse comparison operations Expression_Obj Parser::parse_relation() { + NESTING_GUARD(nestings); advanceToNextToken(); ParserState state(pstate); // parse the left hand side expression @@ -1308,6 +1320,7 @@ namespace Sass { // parse addition and subtraction operations Expression_Obj Parser::parse_expression() { + NESTING_GUARD(nestings); advanceToNextToken(); ParserState state(pstate); // parses multiple add and subtract operations @@ -1351,6 +1364,7 @@ namespace Sass { // parse addition and subtraction operations Expression_Obj Parser::parse_operators() { + NESTING_GUARD(nestings); advanceToNextToken(); ParserState state(pstate); Expression_Obj factor = parse_factor(); @@ -1383,6 +1397,7 @@ namespace Sass { // called from parse_value_schema Expression_Obj Parser::parse_factor() { + NESTING_GUARD(nestings); lex < css_comments >(false); if (lex_css< exactly<'('> >()) { // parse_map may return a list diff --git a/src/parser.hpp b/src/parser.hpp index d481e77b0f..3c32c894ab 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -10,6 +10,15 @@ #include "position.hpp" #include "prelexer.hpp" +#ifndef MAX_NESTING +// Note that this limit is not an exact science +// it depends on various factors, which some are +// not under our control (compile time or even OS +// dependent settings on the available stack size) +// It should fix most common segfault cases though. +#define MAX_NESTING 512 +#endif + struct Lookahead { const char* found; const char* error; @@ -35,13 +44,14 @@ namespace Sass { Position before_token; Position after_token; ParserState pstate; - int indentation; + size_t indentation; + size_t nestings; Token lexed; Parser(Context& ctx, const ParserState& pstate) : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(), - source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0) + source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0), nestings(0) { stack.push_back(Scope::Root); } // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));