Skip to content

Commit f2db048

Browse files
committed
Make parse_css_variable_value non-recursive
Fixes #2658 stack overflow
1 parent e53c7f8 commit f2db048

File tree

4 files changed

+70
-59
lines changed

4 files changed

+70
-59
lines changed

src/parser.cpp

Lines changed: 49 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,71 +1804,63 @@ namespace Sass {
18041804
return schema.detach();
18051805
}
18061806

1807-
String_Schema_Obj Parser::parse_css_variable_value(bool top_level)
1807+
String_Schema_Obj Parser::parse_css_variable_value()
18081808
{
18091809
String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
1810-
String_Schema_Obj tok;
1811-
if (!(tok = parse_css_variable_value_token(top_level))) {
1812-
return {};
1813-
}
1814-
1815-
schema->concat(tok);
1816-
while ((tok = parse_css_variable_value_token(top_level))) {
1817-
schema->concat(tok);
1818-
}
1819-
1820-
return schema.detach();
1821-
}
1822-
1823-
String_Schema_Obj Parser::parse_css_variable_value_token(bool top_level)
1824-
{
1825-
String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
1826-
if (
1827-
(top_level && lex< css_variable_top_level_value >(false)) ||
1828-
(!top_level && lex< css_variable_value >(false))
1829-
) {
1830-
Token str(lexed);
1831-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));
1832-
}
1833-
else if (Expression_Obj tok = lex_interpolation()) {
1834-
if (String_Schema* s = Cast<String_Schema>(tok)) {
1835-
schema->concat(s);
1836-
} else {
1837-
schema->append(tok);
1838-
}
1839-
}
1840-
else if (lex< quoted_string >()) {
1841-
Expression_Obj tok = parse_string();
1842-
if (String_Schema* s = Cast<String_Schema>(tok)) {
1843-
schema->concat(s);
1844-
} else {
1845-
schema->append(tok);
1846-
}
1847-
}
1848-
else {
1849-
if (peek< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {
1850-
if (lex< exactly<'('> >()) {
1851-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("(")));
1852-
if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);
1853-
if (!lex< exactly<')'> >()) css_error("Invalid CSS", " after ", ": expected \")\", was ");
1854-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(")")));
1810+
std::vector<char> brackets;
1811+
while (true) {
1812+
if (
1813+
(brackets.empty() && lex< css_variable_top_level_value >(false)) ||
1814+
(!brackets.empty() && lex< css_variable_value >(false))
1815+
) {
1816+
Token str(lexed);
1817+
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));
1818+
} else if (Expression_Obj tok = lex_interpolation()) {
1819+
if (String_Schema* s = Cast<String_Schema>(tok)) {
1820+
if (s->empty()) break;
1821+
schema->concat(s);
1822+
} else {
1823+
schema->append(tok);
18551824
}
1856-
else if (lex< exactly<'['> >()) {
1857-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("[")));
1858-
if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);
1859-
if (!lex< exactly<']'> >()) css_error("Invalid CSS", " after ", ": expected \"]\", was ");
1860-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("]")));
1825+
} else if (lex< quoted_string >()) {
1826+
Expression_Obj tok = parse_string();
1827+
if (tok.isNull()) break;
1828+
if (String_Schema* s = Cast<String_Schema>(tok)) {
1829+
if (s->empty()) break;
1830+
schema->concat(s);
1831+
} else {
1832+
schema->append(tok);
18611833
}
1862-
else if (lex< exactly<'{'> >()) {
1863-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("{")));
1864-
if (String_Schema_Obj tok = parse_css_variable_value(false)) schema->concat(tok);
1865-
if (!lex< exactly<'}'> >()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");
1866-
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string("}")));
1834+
} else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {
1835+
const char opening_bracket = *(position - 1);
1836+
brackets.push_back(opening_bracket);
1837+
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, opening_bracket)));
1838+
} else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) {
1839+
if (brackets.empty()) break;
1840+
const char closing_bracket = *(match - 1);
1841+
if (brackets.back() != Util::opening_bracket_for(closing_bracket)) {
1842+
std::string message = ": expected \"";
1843+
message += Util::closing_bracket_for(brackets.back());
1844+
message += "\", was ";
1845+
css_error("Invalid CSS", " after ", message);
18671846
}
1847+
lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >();
1848+
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(1, closing_bracket)));
1849+
brackets.pop_back();
1850+
} else {
1851+
break;
18681852
}
18691853
}
18701854

1871-
return schema->length() > 0 ? schema.detach() : NULL;
1855+
if (!brackets.empty()) {
1856+
std::string message = ": expected \"";
1857+
message += Util::closing_bracket_for(brackets.back());
1858+
message += "\", was ";
1859+
css_error("Invalid CSS", " after ", message);
1860+
}
1861+
1862+
if (schema->empty()) return {};
1863+
return schema.detach();
18721864
}
18731865

18741866
Value_Obj Parser::parse_static_value()

src/parser.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@ namespace Sass {
294294
String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true);
295295
String_Obj parse_string();
296296
Value_Obj parse_static_value();
297-
String_Schema_Obj parse_css_variable_value(bool top_level = true);
298-
String_Schema_Obj parse_css_variable_value_token(bool top_level = true);
297+
String_Schema_Obj parse_css_variable_value();
299298
String_Obj parse_ie_property();
300299
String_Obj parse_ie_keyword_arg();
301300
String_Schema_Obj parse_value_schema(const char* stop);

src/util_string.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,23 @@ std::string normalize_decimals(const std::string& str) {
5353
return normalized;
5454
}
5555

56+
char opening_bracket_for(char closing_bracket) {
57+
switch (closing_bracket) {
58+
case ')': return '(';
59+
case ']': return '[';
60+
case '}': return '{';
61+
default: return '\0';
62+
}
63+
}
64+
65+
char closing_bracket_for(char opening_bracket) {
66+
switch (opening_bracket) {
67+
case '(': return ')';
68+
case '[': return ']';
69+
case '{': return '}';
70+
default: return '\0';
71+
}
72+
}
73+
5674
} // namespace Sass
5775
} // namespace Util

src/util_string.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ std::string rtrim(const std::string& str);
1111
std::string normalize_newlines(const std::string& str);
1212
std::string normalize_underscores(const std::string& str);
1313
std::string normalize_decimals(const std::string& str);
14+
char opening_bracket_for(char closing_bracket);
15+
char closing_bracket_for(char opening_bracket);
1416

1517
} // namespace Sass
1618
} // namespace Util

0 commit comments

Comments
 (0)