[regexp] Adhere to the stack limit in the interpreter
This introduces a limit for the interpreter's BacktrackStack to match the limit used by generated code (RegExpStack::kMaximumStackSize). Bug: chromium:1006670 Change-Id: I0b7613698e61257aecca89535ad9109c7e454692 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1821458 Commit-Queue: Jakob Gruber <jgruber@chromium.org> Commit-Queue: Peter Marshall <petermarshall@chromium.org> Auto-Submit: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Peter Marshall <petermarshall@chromium.org> Cr-Commit-Position: refs/heads/master@{#63945}
This commit is contained in:
parent
cf3421008a
commit
256a81671b
@ -12,6 +12,7 @@
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/regexp/regexp-bytecodes.h"
|
||||
#include "src/regexp/regexp-macro-assembler.h"
|
||||
#include "src/regexp/regexp-stack.h" // For kMaximumStackSize.
|
||||
#include "src/regexp/regexp.h"
|
||||
#include "src/strings/unicode.h"
|
||||
#include "src/utils/utils.h"
|
||||
@ -118,7 +119,10 @@ class BacktrackStack {
|
||||
public:
|
||||
BacktrackStack() = default;
|
||||
|
||||
void push(int v) { data_.emplace_back(v); }
|
||||
V8_WARN_UNUSED_RESULT bool push(int v) {
|
||||
data_.emplace_back(v);
|
||||
return (static_cast<int>(data_.size()) <= kMaxSize);
|
||||
}
|
||||
int peek() const {
|
||||
DCHECK(!data_.empty());
|
||||
return data_.back();
|
||||
@ -141,13 +145,17 @@ class BacktrackStack {
|
||||
// static stack-allocated backing store, but small enough not to waste space.
|
||||
static constexpr int kStaticCapacity = 64;
|
||||
|
||||
base::SmallVector<int, kStaticCapacity> data_;
|
||||
using ValueT = int;
|
||||
base::SmallVector<ValueT, kStaticCapacity> data_;
|
||||
|
||||
static constexpr int kMaxSize =
|
||||
RegExpStack::kMaximumStackSize / sizeof(ValueT);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BacktrackStack);
|
||||
};
|
||||
|
||||
IrregexpInterpreter::Result StackOverflow(Isolate* isolate,
|
||||
RegExp::CallOrigin call_origin) {
|
||||
IrregexpInterpreter::Result ThrowStackOverflow(Isolate* isolate,
|
||||
RegExp::CallOrigin call_origin) {
|
||||
CHECK(call_origin == RegExp::CallOrigin::kFromRuntime);
|
||||
// We abort interpreter execution after the stack overflow is thrown, and thus
|
||||
// allow allocation here despite the outer DisallowHeapAllocationScope.
|
||||
@ -156,6 +164,17 @@ IrregexpInterpreter::Result StackOverflow(Isolate* isolate,
|
||||
return IrregexpInterpreter::EXCEPTION;
|
||||
}
|
||||
|
||||
// Only throws if called from the runtime, otherwise just returns the EXCEPTION
|
||||
// status code.
|
||||
IrregexpInterpreter::Result MaybeThrowStackOverflow(
|
||||
Isolate* isolate, RegExp::CallOrigin call_origin) {
|
||||
if (call_origin == RegExp::CallOrigin::kFromRuntime) {
|
||||
return ThrowStackOverflow(isolate, call_origin);
|
||||
} else {
|
||||
return IrregexpInterpreter::EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void UpdateCodeAndSubjectReferences(
|
||||
Isolate* isolate, Handle<ByteArray> code_array,
|
||||
@ -208,7 +227,7 @@ IrregexpInterpreter::Result HandleInterrupts(
|
||||
Handle<String> subject_handle(*subject_string_out, isolate);
|
||||
|
||||
if (js_has_overflowed) {
|
||||
return StackOverflow(isolate, call_origin);
|
||||
return ThrowStackOverflow(isolate, call_origin);
|
||||
} else if (check.InterruptRequested()) {
|
||||
const bool was_one_byte =
|
||||
String::IsOneByteRepresentationUnderneath(*subject_string_out);
|
||||
@ -376,17 +395,23 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
BYTECODE(BREAK) { UNREACHABLE(); }
|
||||
BYTECODE(PUSH_CP) {
|
||||
ADVANCE(PUSH_CP);
|
||||
backtrack_stack.push(current);
|
||||
if (!backtrack_stack.push(current)) {
|
||||
return MaybeThrowStackOverflow(isolate, call_origin);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(PUSH_BT) {
|
||||
ADVANCE(PUSH_BT);
|
||||
backtrack_stack.push(Load32Aligned(pc + 4));
|
||||
if (!backtrack_stack.push(Load32Aligned(pc + 4))) {
|
||||
return MaybeThrowStackOverflow(isolate, call_origin);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(PUSH_REGISTER) {
|
||||
ADVANCE(PUSH_REGISTER);
|
||||
backtrack_stack.push(registers[insn >> BYTECODE_SHIFT]);
|
||||
if (!backtrack_stack.push(registers[insn >> BYTECODE_SHIFT])) {
|
||||
return MaybeThrowStackOverflow(isolate, call_origin);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(SET_REGISTER) {
|
||||
|
@ -73,6 +73,9 @@ class RegExpStack {
|
||||
char* RestoreStack(char* from);
|
||||
void FreeThreadResources() { thread_local_.Free(); }
|
||||
|
||||
// Maximal size of allocated stack area.
|
||||
static constexpr size_t kMaximumStackSize = 64 * MB;
|
||||
|
||||
private:
|
||||
RegExpStack();
|
||||
~RegExpStack();
|
||||
@ -84,9 +87,6 @@ class RegExpStack {
|
||||
// Minimal size of allocated stack area.
|
||||
static const size_t kMinimumStackSize = 1 * KB;
|
||||
|
||||
// Maximal size of allocated stack area.
|
||||
static const size_t kMaximumStackSize = 64 * MB;
|
||||
|
||||
// Structure holding the allocated memory, size and limit.
|
||||
struct ThreadLocal {
|
||||
ThreadLocal() { Clear(); }
|
||||
|
5
test/mjsunit/regress/regress-1006670.js
Normal file
5
test/mjsunit/regress/regress-1006670.js
Normal file
@ -0,0 +1,5 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
assertThrows(() => /(a?;?){4000000}/.exec("a"), RangeError);
|
Loading…
Reference in New Issue
Block a user