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:
Mythri 2018-11-05 17:02:51 +00:00 committed by Commit Bot
parent 578fe72102
commit d056294416
5 changed files with 163 additions and 94 deletions

View File

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

View File

@ -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.

View File

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

View File

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

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