Check for stack overflow when pushing arguments in JSConstructStubGeneric
Bug: chromium:896326 Change-Id: I9257573963f611711edbc48a46a3bacbe12a567d Reviewed-on: https://chromium-review.googlesource.com/c/1305934 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Commit-Queue: Mythri Alle <mythria@chromium.org> Cr-Commit-Position: refs/heads/master@{#57398}
This commit is contained in:
parent
578fe72102
commit
d056294416
@ -168,6 +168,20 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) {
|
||||
__ Jump(lr);
|
||||
}
|
||||
|
||||
void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args,
|
||||
Register scratch, Label* stack_overflow) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
__ LoadRoot(scratch, RootIndex::kRealStackLimit);
|
||||
// Make scratch the space we have left. The stack might already be overflowed
|
||||
// here which will cause scratch to become negative.
|
||||
__ sub(scratch, sp, scratch);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(scratch, Operand(num_args, LSL, kPointerSizeLog2));
|
||||
__ b(le, stack_overflow); // Signed comparison.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The construct stub for ES5 constructor functions and ES6 class constructors.
|
||||
@ -252,6 +266,19 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
// Set up pointer to last argument.
|
||||
__ add(r4, fp, Operand(StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
Label enough_stack_space, stack_overflow;
|
||||
Generate_StackOverflowCheck(masm, r0, r5, &stack_overflow);
|
||||
__ b(&enough_stack_space);
|
||||
|
||||
__ bind(&stack_overflow);
|
||||
// Restore the context from the frame.
|
||||
__ ldr(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset));
|
||||
__ CallRuntime(Runtime::kThrowStackOverflow);
|
||||
// Unreachable code.
|
||||
__ bkpt(0);
|
||||
|
||||
__ bind(&enough_stack_space);
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
Label loop, entry;
|
||||
__ mov(r5, r0);
|
||||
@ -496,21 +523,6 @@ void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
|
||||
__ CallRuntime(Runtime::kThrowConstructedNonConstructable);
|
||||
}
|
||||
|
||||
static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args,
|
||||
Register scratch,
|
||||
Label* stack_overflow) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
__ LoadRoot(scratch, RootIndex::kRealStackLimit);
|
||||
// Make scratch the space we have left. The stack might already be overflowed
|
||||
// here which will cause scratch to become negative.
|
||||
__ sub(scratch, sp, scratch);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(scratch, Operand(num_args, LSL, kPointerSizeLog2));
|
||||
__ b(le, stack_overflow); // Signed comparison.
|
||||
}
|
||||
|
||||
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
|
||||
bool is_construct) {
|
||||
// Called from Generate_JS_Entry
|
||||
|
@ -195,6 +195,44 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) {
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args,
|
||||
Label* stack_overflow) {
|
||||
UseScratchRegisterScope temps(masm);
|
||||
Register scratch = temps.AcquireX();
|
||||
|
||||
// Check the stack for overflow.
|
||||
// We are not trying to catch interruptions (e.g. debug break and
|
||||
// preemption) here, so the "real stack limit" is checked.
|
||||
Label enough_stack_space;
|
||||
__ LoadRoot(scratch, RootIndex::kRealStackLimit);
|
||||
// Make scratch the space we have left. The stack might already be overflowed
|
||||
// here which will cause scratch to become negative.
|
||||
__ Sub(scratch, sp, scratch);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ Cmp(scratch, Operand(num_args, LSL, kPointerSizeLog2));
|
||||
__ B(le, stack_overflow);
|
||||
|
||||
#if defined(V8_OS_WIN)
|
||||
// Simulate _chkstk to extend stack guard page on Windows ARM64.
|
||||
const int kPageSize = 4096;
|
||||
Label chkstk, chkstk_done;
|
||||
Register probe = temps.AcquireX();
|
||||
|
||||
__ Sub(scratch, sp, Operand(num_args, LSL, kPointerSizeLog2));
|
||||
__ Mov(probe, sp);
|
||||
|
||||
// Loop start of stack probe.
|
||||
__ Bind(&chkstk);
|
||||
__ Sub(probe, probe, kPageSize);
|
||||
__ Cmp(probe, scratch);
|
||||
__ B(lo, &chkstk_done);
|
||||
__ Ldrb(xzr, MemOperand(probe));
|
||||
__ B(&chkstk);
|
||||
|
||||
__ Bind(&chkstk_done);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The construct stub for ES5 constructor functions and ES6 class constructors.
|
||||
@ -303,6 +341,19 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
// slots for the arguments. If the number of arguments was odd, the last
|
||||
// argument will overwrite one of the receivers pushed above.
|
||||
__ Bic(x10, x12, 1);
|
||||
|
||||
// Check if we have enough stack space to push all arguments.
|
||||
Label enough_stack_space, stack_overflow;
|
||||
Generate_StackOverflowCheck(masm, x10, &stack_overflow);
|
||||
__ B(&enough_stack_space);
|
||||
|
||||
__ Bind(&stack_overflow);
|
||||
// Restore the context from the frame.
|
||||
__ Ldr(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset));
|
||||
__ CallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ Unreachable();
|
||||
|
||||
__ Bind(&enough_stack_space);
|
||||
__ Claim(x10);
|
||||
|
||||
// Copy the arguments.
|
||||
@ -534,43 +585,6 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
|
||||
}
|
||||
}
|
||||
|
||||
static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args,
|
||||
Label* stack_overflow) {
|
||||
UseScratchRegisterScope temps(masm);
|
||||
Register scratch = temps.AcquireX();
|
||||
|
||||
// Check the stack for overflow.
|
||||
// We are not trying to catch interruptions (e.g. debug break and
|
||||
// preemption) here, so the "real stack limit" is checked.
|
||||
Label enough_stack_space;
|
||||
__ LoadRoot(scratch, RootIndex::kRealStackLimit);
|
||||
// Make scratch the space we have left. The stack might already be overflowed
|
||||
// here which will cause scratch to become negative.
|
||||
__ Sub(scratch, sp, scratch);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ Cmp(scratch, Operand(num_args, LSL, kPointerSizeLog2));
|
||||
__ B(le, stack_overflow);
|
||||
|
||||
#if defined(V8_OS_WIN)
|
||||
// Simulate _chkstk to extend stack guard page on Windows ARM64.
|
||||
const int kPageSize = 4096;
|
||||
Label chkstk, chkstk_done;
|
||||
Register probe = temps.AcquireX();
|
||||
|
||||
__ Sub(scratch, sp, Operand(num_args, LSL, kPointerSizeLog2));
|
||||
__ Mov(probe, sp);
|
||||
|
||||
// Loop start of stack probe.
|
||||
__ Bind(&chkstk);
|
||||
__ Sub(probe, probe, kPageSize);
|
||||
__ Cmp(probe, scratch);
|
||||
__ B(lo, &chkstk_done);
|
||||
__ Ldrb(xzr, MemOperand(probe));
|
||||
__ B(&chkstk);
|
||||
|
||||
__ Bind(&chkstk_done);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Input:
|
||||
// x0: new.target.
|
||||
|
@ -138,6 +138,30 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) {
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args,
|
||||
Register scratch, Label* stack_overflow,
|
||||
bool include_receiver = false) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
ExternalReference real_stack_limit =
|
||||
ExternalReference::address_of_real_stack_limit(masm->isolate());
|
||||
// Compute the space that is left as a negative number in scratch. If
|
||||
// we already overflowed, this will be a positive number.
|
||||
__ mov(scratch, __ ExternalReferenceAsOperand(real_stack_limit, scratch));
|
||||
__ sub(scratch, esp);
|
||||
// Add the size of the arguments.
|
||||
static_assert(kPointerSize == 4,
|
||||
"The next instruction assumes kPointerSize == 4");
|
||||
__ lea(scratch, Operand(scratch, num_args, times_4, 0));
|
||||
if (include_receiver) {
|
||||
__ add(scratch, Immediate(kPointerSize));
|
||||
}
|
||||
// See if we overflowed, i.e. scratch is positive.
|
||||
__ cmp(scratch, Immediate(0));
|
||||
__ j(greater, stack_overflow); // Signed comparison.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The construct stub for ES5 constructor functions and ES6 class constructors.
|
||||
@ -227,6 +251,21 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
// Set up pointer to last argument.
|
||||
__ lea(edi, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
// Check if we have enough stack space to push all arguments.
|
||||
// Argument count in eax. Clobbers ecx.
|
||||
Label enough_stack_space, stack_overflow;
|
||||
Generate_StackOverflowCheck(masm, eax, ecx, &stack_overflow);
|
||||
__ jmp(&enough_stack_space);
|
||||
|
||||
__ bind(&stack_overflow);
|
||||
// Restore context from the frame.
|
||||
__ mov(esi, Operand(ebp, ConstructFrameConstants::kContextOffset));
|
||||
__ CallRuntime(Runtime::kThrowStackOverflow);
|
||||
// This should be unreachable.
|
||||
__ int3();
|
||||
|
||||
__ bind(&enough_stack_space);
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
Label loop, entry;
|
||||
__ mov(ecx, eax);
|
||||
@ -323,29 +362,6 @@ void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
|
||||
__ CallRuntime(Runtime::kThrowConstructedNonConstructable);
|
||||
}
|
||||
|
||||
static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args,
|
||||
Register scratch, Label* stack_overflow,
|
||||
bool include_receiver = false) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
ExternalReference real_stack_limit =
|
||||
ExternalReference::address_of_real_stack_limit(masm->isolate());
|
||||
// Compute the space that is left as a negative number in scratch. If
|
||||
// we already overflowed, this will be a positive number.
|
||||
__ mov(scratch, __ ExternalReferenceAsOperand(real_stack_limit, scratch));
|
||||
__ sub(scratch, esp);
|
||||
// Add the size of the arguments.
|
||||
static_assert(kPointerSize == 4,
|
||||
"The next instruction assumes kPointerSize == 4");
|
||||
__ lea(scratch, Operand(scratch, num_args, times_4, 0));
|
||||
if (include_receiver) {
|
||||
__ add(scratch, Immediate(kPointerSize));
|
||||
}
|
||||
// See if we overflowed, i.e. scratch is positive.
|
||||
__ cmp(scratch, Immediate(0));
|
||||
__ j(greater, stack_overflow); // Signed comparison.
|
||||
}
|
||||
|
||||
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
|
||||
bool is_construct) {
|
||||
|
@ -136,6 +136,26 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) {
|
||||
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
void Generate_StackOverflowCheck(
|
||||
MacroAssembler* masm, Register num_args, Register scratch,
|
||||
Label* stack_overflow,
|
||||
Label::Distance stack_overflow_distance = Label::kFar) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
__ LoadRoot(kScratchRegister, RootIndex::kRealStackLimit);
|
||||
__ movp(scratch, rsp);
|
||||
// Make scratch the space we have left. The stack might already be overflowed
|
||||
// here which will cause scratch to become negative.
|
||||
__ subp(scratch, kScratchRegister);
|
||||
__ sarp(scratch, Immediate(kPointerSizeLog2));
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmpp(scratch, num_args);
|
||||
// Signed comparison.
|
||||
__ j(less_equal, stack_overflow, stack_overflow_distance);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The construct stub for ES5 constructor functions and ES6 class constructors.
|
||||
@ -222,6 +242,21 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||
// Set up pointer to last argument.
|
||||
__ leap(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset));
|
||||
|
||||
// Check if we have enough stack space to push all arguments.
|
||||
// Argument count in rax. Clobbers rcx.
|
||||
Label enough_stack_space, stack_overflow;
|
||||
Generate_StackOverflowCheck(masm, rax, rcx, &stack_overflow, Label::kNear);
|
||||
__ jmp(&enough_stack_space, Label::kNear);
|
||||
|
||||
__ bind(&stack_overflow);
|
||||
// Restore context from the frame.
|
||||
__ movp(rsi, Operand(rbp, ConstructFrameConstants::kContextOffset));
|
||||
__ CallRuntime(Runtime::kThrowStackOverflow);
|
||||
// This should be unreachable.
|
||||
__ int3();
|
||||
|
||||
__ bind(&enough_stack_space);
|
||||
|
||||
// Copy arguments and receiver to the expression stack.
|
||||
Label loop, entry;
|
||||
__ movp(rcx, rax);
|
||||
@ -317,25 +352,6 @@ void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) {
|
||||
__ CallRuntime(Runtime::kThrowConstructedNonConstructable);
|
||||
}
|
||||
|
||||
static void Generate_StackOverflowCheck(
|
||||
MacroAssembler* masm, Register num_args, Register scratch,
|
||||
Label* stack_overflow,
|
||||
Label::Distance stack_overflow_distance = Label::kFar) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
__ LoadRoot(kScratchRegister, RootIndex::kRealStackLimit);
|
||||
__ movp(scratch, rsp);
|
||||
// Make scratch the space we have left. The stack might already be overflowed
|
||||
// here which will cause scratch to become negative.
|
||||
__ subp(scratch, kScratchRegister);
|
||||
__ sarp(scratch, Immediate(kPointerSizeLog2));
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmpp(scratch, num_args);
|
||||
// Signed comparison.
|
||||
__ j(less_equal, stack_overflow, stack_overflow_distance);
|
||||
}
|
||||
|
||||
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
|
||||
bool is_construct) {
|
||||
ProfileEntryHookStub::MaybeCallEntryHook(masm);
|
||||
|
11
test/mjsunit/regress/regress-896326.js
Normal file
11
test/mjsunit/regress/regress-896326.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2018 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() {
|
||||
}
|
||||
|
||||
var large_array = Array(150 * 1024);
|
||||
assertThrows('new f(... large_array)', RangeError);
|
Loading…
Reference in New Issue
Block a user