Commit 0089006f authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[regexp] Apply the backtrack limit in jitted code

.. similar to how it is applied in the interpreter. We reserve a stack
slot for the backtrack count, increment it on each backtrack, and fail
if the limit is hit.

Bug: v8:9695
Change-Id: I835888c612d6c8bfa2f34e73ab8c8241dcabc6ed
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1864938Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64426}
parent ecf55546
......@@ -154,6 +154,19 @@ void RegExpMacroAssemblerARM::AdvanceRegister(int reg, int by) {
void RegExpMacroAssemblerARM::Backtrack() {
CheckPreemption();
if (has_backtrack_limit()) {
Label next;
__ ldr(r0, MemOperand(frame_pointer(), kBacktrackCount));
__ add(r0, r0, Operand(1));
__ str(r0, MemOperand(frame_pointer(), kBacktrackCount));
__ cmp(r0, Operand(backtrack_limit()));
__ b(ne, &next);
// Exceeded limits are treated as a failed match.
Fail();
__ bind(&next);
}
// Pop Code offset from backtrack stack, add Code and jump to location.
Pop(r0);
__ add(pc, r0, Operand(code_pointer()));
......@@ -642,9 +655,16 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
// Set frame pointer in space for it if this is not a direct call
// from generated code.
__ add(frame_pointer(), sp, Operand(4 * kPointerSize));
STATIC_ASSERT(kSuccessfulCaptures == kInputString - kSystemPointerSize);
__ mov(r0, Operand::Zero());
__ push(r0); // Make room for success counter and initialize it to 0.
STATIC_ASSERT(kStringStartMinusOne ==
kSuccessfulCaptures - kSystemPointerSize);
__ push(r0); // Make room for "string start - 1" constant.
STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize);
__ push(r0); // The backtrack counter.
// Check if we have space on the stack for registers.
Label stack_limit_hit;
Label stack_ok;
......
......@@ -118,8 +118,9 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
// the frame in GetCode.
static const int kSuccessfulCaptures = kInputString - kPointerSize;
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize;
// First register address. Following registers are below it on the stack.
static const int kRegisterZero = kStringStartMinusOne - kPointerSize;
static const int kRegisterZero = kBacktrackCount - kSystemPointerSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
......
......@@ -187,6 +187,21 @@ void RegExpMacroAssemblerARM64::AdvanceRegister(int reg, int by) {
void RegExpMacroAssemblerARM64::Backtrack() {
CheckPreemption();
if (has_backtrack_limit()) {
Label next;
UseScratchRegisterScope temps(masm_);
Register scratch = temps.AcquireW();
__ Ldr(scratch, MemOperand(frame_pointer(), kBacktrackCount));
__ Add(scratch, scratch, 1);
__ Str(scratch, MemOperand(frame_pointer(), kBacktrackCount));
__ Cmp(scratch, Operand(backtrack_limit()));
__ B(ne, &next);
// Exceeded limits are treated as a failed match.
Fail();
__ bind(&next);
}
Pop(w10);
__ Add(x10, code_pointer(), Operand(w10, UXTW));
__ Br(x10);
......@@ -738,13 +753,15 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
__ Mov(output_array(), x4);
// Set the number of registers we will need to allocate, that is:
// - success_counter (X register)
// - kSuccessCounter / success_counter (X register)
// - kBacktrackCount (X register)
// - (num_registers_ - kNumCachedRegisters) (W registers)
int num_wreg_to_allocate = num_registers_ - kNumCachedRegisters;
// Do not allocate registers on the stack if they can all be cached.
if (num_wreg_to_allocate < 0) { num_wreg_to_allocate = 0; }
// Make room for the success_counter.
num_wreg_to_allocate += 2;
// Make room for the success_counter and kBacktrackCount. Each X (64-bit)
// register is equivalent to two W (32-bit) registers.
num_wreg_to_allocate += 2 + 2;
// Make sure the stack alignment will be respected.
int alignment = masm_->ActivationFrameAlignment();
......@@ -785,8 +802,9 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
// Allocate space on stack.
__ Claim(num_wreg_to_allocate, kWRegSize);
// Initialize success_counter with 0.
// Initialize success_counter and kBacktrackCount with 0.
__ Str(wzr, MemOperand(frame_pointer(), kSuccessCounter));
__ Str(wzr, MemOperand(frame_pointer(), kBacktrackCount));
// Find negative length (offset of start relative to end).
__ Sub(x10, input_start(), input_end());
......
......@@ -119,11 +119,12 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM64
// When adding local variables remember to push space for them in
// the frame in GetCode.
static const int kSuccessCounter = kInput - kSystemPointerSize;
static const int kBacktrackCount = kSuccessCounter - kSystemPointerSize;
// First position register address on the stack. Following positions are
// below it. A position is a 32 bit value.
static const int kFirstRegisterOnStack = kSuccessCounter - kWRegSize;
static const int kFirstRegisterOnStack = kBacktrackCount - kWRegSize;
// A capture is a 64 bit value holding two position.
static const int kFirstCaptureOnStack = kSuccessCounter - kXRegSize;
static const int kFirstCaptureOnStack = kBacktrackCount - kXRegSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
......
......@@ -142,6 +142,17 @@ void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) {
void RegExpMacroAssemblerIA32::Backtrack() {
CheckPreemption();
if (has_backtrack_limit()) {
Label next;
__ inc(Operand(ebp, kBacktrackCount));
__ cmp(Operand(ebp, kBacktrackCount), Immediate(backtrack_limit()));
__ j(not_equal, &next);
// Exceeded limits are treated as a failed match.
Fail();
__ bind(&next);
}
// Pop Code offset from backtrack stack, add Code and jump to location.
Pop(ebx);
__ add(ebx, Immediate(masm_->CodeObject()));
......@@ -678,8 +689,14 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
__ push(esi);
__ push(edi);
__ push(ebx); // Callee-save on MacOS.
STATIC_ASSERT(kSuccessfulCaptures == kBackup_ebx - kSystemPointerSize);
__ push(Immediate(0)); // Number of successful matches in a global regexp.
STATIC_ASSERT(kStringStartMinusOne ==
kSuccessfulCaptures - kSystemPointerSize);
__ push(Immediate(0)); // Make room for "string start - 1" constant.
STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize);
__ push(Immediate(0)); // The backtrack counter.
// Check if we have space on the stack for registers.
Label stack_limit_hit;
......
......@@ -120,8 +120,9 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerIA32
static const int kSuccessfulCaptures = kBackup_ebx - kSystemPointerSize;
static const int kStringStartMinusOne =
kSuccessfulCaptures - kSystemPointerSize;
static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize;
// First register address. Following registers are below it on the stack.
static const int kRegisterZero = kStringStartMinusOne - kSystemPointerSize;
static const int kRegisterZero = kBacktrackCount - kSystemPointerSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
......
......@@ -172,6 +172,10 @@ class RegExpMacroAssembler {
void set_slow_safe(bool ssc) { slow_safe_compiler_ = ssc; }
bool slow_safe() { return slow_safe_compiler_; }
void set_backtrack_limit(uint32_t backtrack_limit) {
backtrack_limit_ = backtrack_limit;
}
enum GlobalMode {
NOT_GLOBAL,
GLOBAL_NO_ZERO_LENGTH_CHECK,
......@@ -190,8 +194,15 @@ class RegExpMacroAssembler {
Isolate* isolate() const { return isolate_; }
Zone* zone() const { return zone_; }
protected:
bool has_backtrack_limit() const {
return backtrack_limit_ != JSRegExp::kNoBacktrackLimit;
}
uint32_t backtrack_limit() const { return backtrack_limit_; }
private:
bool slow_safe_compiler_;
uint32_t backtrack_limit_ = JSRegExp::kNoBacktrackLimit;
GlobalMode global_mode_;
Isolate* isolate_;
Zone* zone_;
......
......@@ -76,7 +76,8 @@ class RegExpImpl final : public AllStatic {
// Returns true on success, false on failure.
static bool Compile(Isolate* isolate, Zone* zone, RegExpCompileData* input,
JSRegExp::Flags flags, Handle<String> pattern,
Handle<String> sample_subject, bool is_one_byte);
Handle<String> sample_subject, bool is_one_byte,
uint32_t backtrack_limit);
// For acting on the JSRegExp data FixedArray.
static int IrregexpMaxRegisterCount(FixedArray re);
......@@ -404,7 +405,7 @@ bool RegExpImpl::CompileIrregexp(Isolate* isolate, Handle<JSRegExp> re,
: RegExpCompilationTarget::kNative;
const bool compilation_succeeded =
Compile(isolate, &zone, &compile_data, flags, pattern, sample_subject,
is_one_byte);
is_one_byte, re->BacktrackLimit());
if (!compilation_succeeded) {
DCHECK(!compile_data.error.is_null());
ThrowRegExpException(isolate, re, compile_data.error);
......@@ -733,12 +734,14 @@ bool RegExp::CompileForTesting(Isolate* isolate, Zone* zone,
Handle<String> sample_subject,
bool is_one_byte) {
return RegExpImpl::Compile(isolate, zone, data, flags, pattern,
sample_subject, is_one_byte);
sample_subject, is_one_byte,
JSRegExp::kNoBacktrackLimit);
}
bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data,
JSRegExp::Flags flags, Handle<String> pattern,
Handle<String> sample_subject, bool is_one_byte) {
Handle<String> sample_subject, bool is_one_byte,
uint32_t backtrack_limit) {
if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
data->error =
isolate->factory()->NewStringFromAsciiChecked("RegExp too big");
......@@ -858,6 +861,7 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data,
}
macro_assembler->set_slow_safe(TooMuchRegExpCode(isolate, pattern));
macro_assembler->set_backtrack_limit(backtrack_limit);
// Inserted here, instead of in Assembler, because it depends on information
// in the AST that isn't replicated in the Node structure.
......
......@@ -15,6 +15,7 @@ class RegExpTree;
enum class RegExpCompilationTarget : int { kBytecode, kNative };
// TODO(jgruber): Do not expose in regexp.h.
// TODO(jgruber): Consider splitting between ParseData and CompileData.
struct RegExpCompileData {
// The parsed AST as produced by the RegExpParser.
......
......@@ -151,6 +151,17 @@ void RegExpMacroAssemblerX64::AdvanceRegister(int reg, int by) {
void RegExpMacroAssemblerX64::Backtrack() {
CheckPreemption();
if (has_backtrack_limit()) {
Label next;
__ incq(Operand(rbp, kBacktrackCount));
__ cmpq(Operand(rbp, kBacktrackCount), Immediate(backtrack_limit()));
__ j(not_equal, &next);
// Exceeded limits are treated as a failed match.
Fail();
__ bind(&next);
}
// Pop Code offset from backtrack stack, add Code and jump to location.
Pop(rbx);
__ addq(rbx, code_object_pointer());
......@@ -713,8 +724,14 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
__ pushq(rbx); // Callee-save
#endif
STATIC_ASSERT(kSuccessfulCaptures ==
kLastCalleeSaveRegister - kSystemPointerSize);
__ Push(Immediate(0)); // Number of successful matches in a global regexp.
STATIC_ASSERT(kStringStartMinusOne ==
kSuccessfulCaptures - kSystemPointerSize);
__ Push(Immediate(0)); // Make room for "string start - 1" constant.
STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize);
__ Push(Immediate(0)); // The backtrack counter.
// Check if we have space on the stack for registers.
Label stack_limit_hit;
......
......@@ -146,15 +146,16 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
static const int kLastCalleeSaveRegister = kBackup_rbx;
#endif
static const int kSuccessfulCaptures =
kLastCalleeSaveRegister - kSystemPointerSize;
// When adding local variables remember to push space for them in
// the frame in GetCode.
static const int kSuccessfulCaptures =
kLastCalleeSaveRegister - kSystemPointerSize;
static const int kStringStartMinusOne =
kSuccessfulCaptures - kSystemPointerSize;
static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize;
// First register address. Following registers are below it on the stack.
static const int kRegisterZero = kStringStartMinusOne - kSystemPointerSize;
static const int kRegisterZero = kBacktrackCount - kSystemPointerSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --regexp-interpret-all
// Flags: --allow-natives-syntax
const kNoBacktrackLimit = 0; // To match JSRegExp::kNoBacktrackLimit.
const re0 = %NewRegExpWithBacktrackLimit("(\\d+)+x", "", kNoBacktrackLimit);
......
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