From b5875700e2d54451018cdfb05b3e071dcee90078 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Sun, 12 Nov 2023 20:30:32 +0000 Subject: [PATCH] LibJS: Don't hang when parsing invalid destructuring assignment target Previously, certain crafted input could cause the JS parser to hang, as it repeatedly tried to parse an EOF token after hitting an "invalid destructuring assignment target" error. This change ensures that we stop parsing after hitting this error condition. --- Userland/Libraries/LibJS/Parser.cpp | 22 +++++++++++++------ ...invalid-destructuring-assignment-target.js | 5 +++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 Userland/Libraries/LibJS/Tests/parser-invalid-destructuring-assignment-target.js diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 0d88f47982..b787ae1f87 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -3061,12 +3061,14 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicat if (allow_member_expressions == AllowMemberExpressions::Yes && is_rest) { auto expression_position = position(); auto expression = parse_expression(2, Associativity::Right, { TokenType::Equals }); - if (is(*expression)) + if (is(*expression)) { alias = static_ptr_cast(expression); - else if (is(*expression)) + } else if (is(*expression)) { name = static_ptr_cast(expression); - else + } else { syntax_error("Invalid destructuring assignment target", expression_position); + return {}; + } } else if (match_identifier_name() || match(TokenType::StringLiteral) || match(TokenType::NumericLiteral) || match(TokenType::BigIntLiteral)) { if (match(TokenType::StringLiteral) || match(TokenType::NumericLiteral)) needs_alias = true; @@ -3099,16 +3101,19 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicat auto expression_position = position(); auto expression = parse_expression(2, Associativity::Right, { TokenType::Equals }); if (is(*expression) || is(*expression)) { - if (auto synthesized_binding_pattern = synthesize_binding_pattern(*expression)) + if (auto synthesized_binding_pattern = synthesize_binding_pattern(*expression)) { alias = synthesized_binding_pattern.release_nonnull(); - else + } else { syntax_error("Invalid destructuring assignment target", expression_position); + return {}; + } } else if (is(*expression)) { alias = static_ptr_cast(expression); } else if (is(*expression)) { alias = static_ptr_cast(expression); } else { syntax_error("Invalid destructuring assignment target", expression_position); + return {}; } } else if (match(TokenType::CurlyOpen) || match(TokenType::BracketOpen)) { auto binding_pattern = parse_binding_pattern(allow_duplicates, allow_member_expressions); @@ -3131,16 +3136,19 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicat auto expression = parse_expression(2, Associativity::Right, { TokenType::Equals }); if (is(*expression) || is(*expression)) { - if (auto synthesized_binding_pattern = synthesize_binding_pattern(*expression)) + if (auto synthesized_binding_pattern = synthesize_binding_pattern(*expression)) { alias = synthesized_binding_pattern.release_nonnull(); - else + } else { syntax_error("Invalid destructuring assignment target", expression_position); + return {}; + } } else if (is(*expression)) { alias = static_ptr_cast(expression); } else if (is(*expression)) { alias = static_ptr_cast(expression); } else { syntax_error("Invalid destructuring assignment target", expression_position); + return {}; } } else if (match(TokenType::BracketOpen) || match(TokenType::CurlyOpen)) { auto pattern = parse_binding_pattern(allow_duplicates, allow_member_expressions); diff --git a/Userland/Libraries/LibJS/Tests/parser-invalid-destructuring-assignment-target.js b/Userland/Libraries/LibJS/Tests/parser-invalid-destructuring-assignment-target.js new file mode 100644 index 0000000000..6ea83aa62d --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/parser-invalid-destructuring-assignment-target.js @@ -0,0 +1,5 @@ +test("Assigning to an invalid destructuring assignment target should fail immediately", () => { + expect(() => { + eval("[[function=a{1,}="); + }).toThrow(SyntaxError); +});