Commit 6627638a authored by Jakob Gruber's avatar Jakob Gruber Committed by V8 LUCI CQ

[parser] Integrate regexp parser into stack overflow handling

If a stack overflow occurs inside the regexp parser, propagate that
information to the parser.

Bug: v8:896,chromium:1243989
Change-Id: I5ced27ff968ad97764e156643e1980b3a722af1a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3127717
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76568}
parent 196ce3d0
......@@ -1149,7 +1149,7 @@ class ParserBase {
return scanner()->NextSymbol(ast_value_factory());
}
bool ValidateRegExpLiteral(const AstRawString* pattern, RegExpFlags flags,
const char** error_message);
RegExpError* regexp_error);
ExpressionT ParseRegExpLiteral();
ExpressionT ParseBindingPattern();
......@@ -1758,7 +1758,7 @@ ParserBase<Impl>::ParsePropertyOrPrivatePropertyName() {
template <typename Impl>
bool ParserBase<Impl>::ValidateRegExpLiteral(const AstRawString* pattern,
RegExpFlags flags,
const char** error_message) {
RegExpError* regexp_error) {
// TODO(jgruber): If already validated in the preparser, skip validation in
// the parser.
DisallowGarbageCollection no_gc;
......@@ -1766,11 +1766,11 @@ bool ParserBase<Impl>::ValidateRegExpLiteral(const AstRawString* pattern,
if (pattern->is_one_byte()) {
return RegExp::VerifySyntax(zone(), stack_limit(),
static_cast<const uint8_t*>(d),
pattern->length(), flags, error_message, no_gc);
pattern->length(), flags, regexp_error, no_gc);
} else {
return RegExp::VerifySyntax(zone(), stack_limit(),
reinterpret_cast<const uint16_t*>(d),
pattern->length(), flags, error_message, no_gc);
pattern->length(), flags, regexp_error, no_gc);
}
}
......@@ -1791,9 +1791,11 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral() {
return impl()->FailureExpression();
}
Next();
const char* error_message;
if (!ValidateRegExpLiteral(js_pattern, flags.value(), &error_message)) {
ReportMessage(MessageTemplate::kMalformedRegExp, js_pattern, error_message);
RegExpError regexp_error;
if (!ValidateRegExpLiteral(js_pattern, flags.value(), &regexp_error)) {
if (RegExpErrorIsStackOverflow(regexp_error)) set_stack_overflow();
ReportMessage(MessageTemplate::kMalformedRegExp, js_pattern,
RegExpErrorString(regexp_error));
return impl()->FailureExpression();
}
return factory()->NewRegExpLiteral(js_pattern, flags.value(), pos);
......
......@@ -53,6 +53,11 @@ enum class RegExpError : uint32_t {
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
inline constexpr bool RegExpErrorIsStackOverflow(RegExpError error) {
return error == RegExpError::kStackOverflow ||
error == RegExpError::kAnalysisStackOverflow;
}
} // namespace internal
} // namespace v8
......
......@@ -111,22 +111,22 @@ bool RegExp::CanGenerateBytecode() {
template <class CharT>
bool RegExp::VerifySyntax(Zone* zone, uintptr_t stack_limit, const CharT* input,
int input_length, RegExpFlags flags,
const char** error_message_out,
RegExpError* regexp_error_out,
const DisallowGarbageCollection& no_gc) {
RegExpCompileData data;
bool pattern_is_valid = RegExpParser::VerifyRegExpSyntax(
zone, stack_limit, input, input_length, flags, &data, no_gc);
if (!pattern_is_valid) *error_message_out = RegExpErrorString(data.error);
*regexp_error_out = data.error;
return pattern_is_valid;
}
template bool RegExp::VerifySyntax<uint8_t>(Zone*, uintptr_t, const uint8_t*,
int, RegExpFlags,
const char** error_message_out,
RegExpError* regexp_error_out,
const DisallowGarbageCollection&);
template bool RegExp::VerifySyntax<base::uc16>(
Zone*, uintptr_t, const base::uc16*, int, RegExpFlags,
const char** error_message_out, const DisallowGarbageCollection&);
RegExpError* regexp_error_out, const DisallowGarbageCollection&);
MaybeHandle<Object> RegExp::ThrowRegExpException(Isolate* isolate,
Handle<JSRegExp> re,
......
......@@ -71,11 +71,11 @@ class RegExp final : public AllStatic {
static bool CanGenerateBytecode();
// Verify the given pattern, i.e. check that parsing succeeds. If
// verification fails, `error_message_out` is set.
// verification fails, `regexp_error_out` is set.
template <class CharT>
static bool VerifySyntax(Zone* zone, uintptr_t stack_limit,
const CharT* input, int input_length,
RegExpFlags flags, const char** error_message_out,
RegExpFlags flags, RegExpError* regexp_error_out,
const DisallowGarbageCollection& no_gc);
// Parses the RegExp pattern and prepares the JSRegExp object with
......
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --stack-size=100
function f(x) {
new x.Uint16Array();
function h(y) { /[\cA]/; }
}
let i = 0;
function g() {
try { g(); } catch (e) {}
if (i++ > 200) return; // The original error was at i == 116.
f();
}
f(this);
g();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment