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