[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:
Jakob Gruber 2019-09-24 12:55:20 +02:00 committed by Commit Bot
parent cf3421008a
commit 256a81671b
3 changed files with 41 additions and 11 deletions

View File

@ -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) {

View File

@ -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(); }

View 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);