Skip to content
Permalink
Browse files

Make `parse_css_variable_value` non-recursive

Fixes #2658 stack overflow
  • Loading branch information...
glebm committed Apr 14, 2019
1 parent e53c7f8 commit f2db04883e5fff4e03777dcc1eb60d4373c45be1
Showing with 70 additions and 59 deletions.
  1. +49 −57 src/parser.cpp
  2. +1 −2 src/parser.hpp
  3. +18 −0 src/util_string.cpp
  4. +2 −0 src/util_string.hpp
@@ -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<String_Schema>(tok)) {
schema->concat(s);
} else {
schema->append(tok);
}
}
else if (lex< quoted_string >()) {
Expression_Obj tok = parse_string();
if (String_Schema* s = Cast<String_Schema>(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<char> 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<String_Schema>(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<String_Schema>(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()
@@ -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);
@@ -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
@@ -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

0 comments on commit f2db048

Please sign in to comment.
You can’t perform that action at this time.