From f2db04883e5fff4e03777dcc1eb60d4373c45be1 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 14 Apr 2019 15:33:36 +0100 Subject: [PATCH] Make `parse_css_variable_value` non-recursive Fixes #2658 stack overflow --- src/parser.cpp | 106 ++++++++++++++++++++------------------------ src/parser.hpp | 3 +- src/util_string.cpp | 18 ++++++++ src/util_string.hpp | 2 + 4 files changed, 70 insertions(+), 59 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index cfb389d48..2afd12cc3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1804,71 +1804,63 @@ namespace Sass { return schema.detach(); } - String_Schema_Obj Parser::parse_css_variable_value(bool top_level) + String_Schema_Obj Parser::parse_css_variable_value() { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); - String_Schema_Obj tok; - if (!(tok = parse_css_variable_value_token(top_level))) { - return {}; - } - - schema->concat(tok); - while ((tok = parse_css_variable_value_token(top_level))) { - schema->concat(tok); - } - - return schema.detach(); - } - - String_Schema_Obj Parser::parse_css_variable_value_token(bool top_level) - { - String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); - if ( - (top_level && lex< css_variable_top_level_value >(false)) || - (!top_level && lex< css_variable_value >(false)) - ) { - Token str(lexed); - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str)); - } - else if (Expression_Obj tok = lex_interpolation()) { - if (String_Schema* s = Cast(tok)) { - schema->concat(s); - } else { - schema->append(tok); - } - } - else if (lex< quoted_string >()) { - Expression_Obj tok = parse_string(); - if (String_Schema* s = Cast(tok)) { - schema->concat(s); - } else { - schema->append(tok); - } - } - else { - if (peek< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) { - if (lex< exactly<'('> >()) { - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("("))); - if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok); - if (!lex< exactly<')'> >()) css_error("Invalid CSS", " after ", ": expected \")\", was "); - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(")"))); + std::vector brackets; + while (true) { + if ( + (brackets.empty() && lex< css_variable_top_level_value >(false)) || + (!brackets.empty() && lex< css_variable_value >(false)) + ) { + Token str(lexed); + schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str)); + } else if (Expression_Obj tok = lex_interpolation()) { + if (String_Schema* s = Cast(tok)) { + if (s->empty()) break; + schema->concat(s); + } else { + schema->append(tok); } - else if (lex< exactly<'['> >()) { - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("["))); - if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok); - if (!lex< exactly<']'> >()) css_error("Invalid CSS", " after ", ": expected \"]\", was "); - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("]"))); + } else if (lex< quoted_string >()) { + Expression_Obj tok = parse_string(); + if (tok.isNull()) break; + if (String_Schema* s = Cast(tok)) { + if (s->empty()) break; + schema->concat(s); + } else { + schema->append(tok); } - else if (lex< exactly<'{'> >()) { - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("{"))); - if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok); - if (!lex< exactly<'}'> >()) css_error("Invalid CSS", " after ", ": expected \"}\", was "); - schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("}"))); + } else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) { + const char opening_bracket = *(position - 1); + brackets.push_back(opening_bracket); + schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, opening_bracket))); + } else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) { + if (brackets.empty()) break; + const char closing_bracket = *(match - 1); + if (brackets.back() != Util::opening_bracket_for(closing_bracket)) { + std::string message = ": expected \""; + message += Util::closing_bracket_for(brackets.back()); + message += "\", was "; + css_error("Invalid CSS", " after ", message); } + lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >(); + schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, closing_bracket))); + brackets.pop_back(); + } else { + break; } } - return schema->length() > 0 ? schema.detach() : NULL; + if (!brackets.empty()) { + std::string message = ": expected \""; + message += Util::closing_bracket_for(brackets.back()); + message += "\", was "; + css_error("Invalid CSS", " after ", message); + } + + if (schema->empty()) return {}; + return schema.detach(); } Value_Obj Parser::parse_static_value() diff --git a/src/parser.hpp b/src/parser.hpp index 8b74cb51c..05f81d130 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -294,8 +294,7 @@ namespace Sass { String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true); String_Obj parse_string(); Value_Obj parse_static_value(); - String_Schema_Obj parse_css_variable_value(bool top_level = true); - String_Schema_Obj parse_css_variable_value_token(bool top_level = true); + String_Schema_Obj parse_css_variable_value(); String_Obj parse_ie_property(); String_Obj parse_ie_keyword_arg(); String_Schema_Obj parse_value_schema(const char* stop); diff --git a/src/util_string.cpp b/src/util_string.cpp index ea56402ca..8af7918e7 100644 --- a/src/util_string.cpp +++ b/src/util_string.cpp @@ -53,5 +53,23 @@ std::string normalize_decimals(const std::string& str) { return normalized; } +char opening_bracket_for(char closing_bracket) { + switch (closing_bracket) { + case ')': return '('; + case ']': return '['; + case '}': return '{'; + default: return '\0'; + } +} + +char closing_bracket_for(char opening_bracket) { + switch (opening_bracket) { + case '(': return ')'; + case '[': return ']'; + case '{': return '}'; + default: return '\0'; + } +} + } // namespace Sass } // namespace Util diff --git a/src/util_string.hpp b/src/util_string.hpp index 66109d30f..9f3795c4b 100644 --- a/src/util_string.hpp +++ b/src/util_string.hpp @@ -11,6 +11,8 @@ std::string rtrim(const std::string& str); std::string normalize_newlines(const std::string& str); std::string normalize_underscores(const std::string& str); std::string normalize_decimals(const std::string& str); +char opening_bracket_for(char closing_bracket); +char closing_bracket_for(char opening_bracket); } // namespace Sass } // namespace Util