From eb74781a2d9ecb25bc35b94ff20193bb59a9a9f3 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 7 Aug 2025 12:35:16 -0400 Subject: [PATCH] LibJS: Keep the lookahead lexer alive after parsing its next token Currently, the lexer holds a ByteString, which is always heap-allocated. When we create a copy of the lexer for the lookahead token, that token will outlive the lexer copy. The token holds a couple of string views into the lexer's source string. This is fine for now, because the source string will be kept alive by the original lexer. But if the lexer were to hold a String or Utf16String, short strings will be stored on the stack due to SSO. Thus the token will hold views into released stack data. We need to keep the lookahead lexer alive to prevent UAF on views into its source string. --- Libraries/LibJS/Parser.cpp | 16 +++++----------- Libraries/LibJS/Parser.h | 3 ++- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 7458131a86f..7e64c775255 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -4266,18 +4266,12 @@ bool Parser::match_declaration(AllowUsingDeclaration allow_using) const || type == TokenType::Let; } -Token Parser::next_token(size_t steps) const +Token Parser::next_token() const { - Lexer lookahead_lexer = m_state.lexer; - - Token lookahead_token; - - while (steps > 0) { - lookahead_token = lookahead_lexer.next(); - steps--; - } - - return lookahead_token; + // We need to keep the lookahead lexer alive to prevent UAF on the lookahead token, as the token may hold a view + // into a short string stored on the stack. + m_state.lookahead_lexer = m_state.lexer; + return m_state.lookahead_lexer->next(); } bool Parser::try_match_let_declaration() const diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index c9d983ce939..edf4252d743 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -257,7 +257,7 @@ private: RefPtr synthesize_binding_pattern(Expression const& expression); - Token next_token(size_t steps = 1) const; + Token next_token() const; void check_identifier_name_for_assignment_validity(FlyString const&, bool force_strict = false); @@ -302,6 +302,7 @@ private: struct ParserState { Lexer lexer; + mutable Optional lookahead_lexer; Token current_token; bool previous_token_was_period { false }; Vector errors;