Commit 7fb31bdb authored by mstarzinger's avatar mstarzinger Committed by Commit bot

Make Simulator respect C stack limits as well.

The simulator uses a separate JS stack, exhaustion of the C stack
however is not caught by JS limit checks. This change now lowers the
limit of the JS stack accordingly on function calls.

R=mvstanton@chromium.org
BUG=chromium:522380
TEST=mjsunit/regress/regress-crbug-522380
LOG=n

Review URL: https://codereview.chromium.org/1314623002

Cr-Commit-Position: refs/heads/master@{#30334}
parent c9f3d892
......@@ -1227,9 +1227,15 @@ void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) {
// Returns the limit of the stack area to enable checking for stack overflows.
uintptr_t Simulator::StackLimit() const {
// Leave a safety margin of 1024 bytes to prevent overrunning the stack when
// pushing values.
uintptr_t Simulator::StackLimit(uintptr_t c_limit) const {
// The simulator uses a separate JS stack. If we have exhausted the C stack,
// we also drop down the JS limit to reflect the exhaustion on the JS stack.
if (GetCurrentStackPosition() < c_limit) {
return reinterpret_cast<uintptr_t>(get_sp());
}
// Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes
// to prevent overrunning the stack when pushing values.
return reinterpret_cast<uintptr_t>(stack_) + 1024;
}
......@@ -4009,6 +4015,9 @@ void Simulator::Execute() {
void Simulator::CallInternal(byte* entry) {
// Adjust JS-based stack limit to C-based stack limit.
isolate_->stack_guard()->AdjustStackLimitForSimulator();
// Prepare to execute the code at entry
set_register(pc, reinterpret_cast<int32_t>(entry));
// Put down marker for end of simulation. The simulator will stop simulation
......
......@@ -181,12 +181,12 @@ class Simulator {
void set_pc(int32_t value);
int32_t get_pc() const;
Address get_sp() {
Address get_sp() const {
return reinterpret_cast<Address>(static_cast<intptr_t>(get_register(sp)));
}
// Accessor to the internal simulator stack area.
uintptr_t StackLimit() const;
uintptr_t StackLimit(uintptr_t c_limit) const;
// Executes ARM instructions until the PC reaches end_sim_pc.
void Execute();
......@@ -439,15 +439,14 @@ class Simulator {
// The simulator has its own stack. Thus it has a different stack limit from
// the C-based native code. Setting the c_limit to indicate a very small
// stack cause stack overflow errors, since the simulator ignores the input.
// This is unlikely to be an issue in practice, though it might cause testing
// trouble down the line.
// the C-based native code. The JS-based limit normally points near the end of
// the simulator stack. When the C-based limit is exhausted we reflect that by
// lowering the JS-based limit as well, to make stack checks trigger.
class SimulatorStack : public v8::internal::AllStatic {
public:
static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
uintptr_t c_limit) {
return Simulator::current(isolate)->StackLimit();
return Simulator::current(isolate)->StackLimit(c_limit);
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
......
......@@ -222,6 +222,9 @@ int64_t Simulator::CallRegExp(byte* entry,
void Simulator::CheckPCSComplianceAndRun() {
// Adjust JS-based stack limit to C-based stack limit.
isolate_->stack_guard()->AdjustStackLimitForSimulator();
#ifdef DEBUG
CHECK_EQ(kNumberOfCalleeSavedRegisters, kCalleeSaved.Count());
CHECK_EQ(kNumberOfCalleeSavedFPRegisters, kCalleeSavedFP.Count());
......@@ -332,9 +335,15 @@ uintptr_t Simulator::PopAddress() {
// Returns the limit of the stack area to enable checking for stack overflows.
uintptr_t Simulator::StackLimit() const {
// Leave a safety margin of 1024 bytes to prevent overrunning the stack when
// pushing values.
uintptr_t Simulator::StackLimit(uintptr_t c_limit) const {
// The simulator uses a separate JS stack. If we have exhausted the C stack,
// we also drop down the JS limit to reflect the exhaustion on the JS stack.
if (GetCurrentStackPosition() < c_limit) {
return reinterpret_cast<uintptr_t>(get_sp());
}
// Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes
// to prevent overrunning the stack when pushing values.
return stack_limit_ + 1024;
}
......
......@@ -266,7 +266,7 @@ class Simulator : public DecoderVisitor {
uintptr_t PopAddress();
// Accessor to the internal simulator stack area.
uintptr_t StackLimit() const;
uintptr_t StackLimit(uintptr_t c_limit) const;
void ResetState();
......@@ -401,7 +401,7 @@ class Simulator : public DecoderVisitor {
}
Instruction* lr() { return reg<Instruction*>(kLinkRegCode); }
Address get_sp() { return reg<Address>(31, Reg31IsStackPointer); }
Address get_sp() const { return reg<Address>(31, Reg31IsStackPointer); }
template<typename T>
T fpreg(unsigned code) const {
......@@ -882,13 +882,14 @@ class Simulator : public DecoderVisitor {
// The simulator has its own stack. Thus it has a different stack limit from
// the C-based native code.
// See also 'class SimulatorStack' in arm/simulator-arm.h.
// the C-based native code. The JS-based limit normally points near the end of
// the simulator stack. When the C-based limit is exhausted we reflect that by
// lowering the JS-based limit as well, to make stack checks trigger.
class SimulatorStack : public v8::internal::AllStatic {
public:
static uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
uintptr_t c_limit) {
return Simulator::current(isolate)->StackLimit();
return Simulator::current(isolate)->StackLimit(c_limit);
}
static uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
......
......@@ -341,14 +341,6 @@ MaybeHandle<Object> Execution::TryGetConstructorDelegate(
}
void StackGuard::EnableInterrupts() {
ExecutionAccess access(isolate_);
if (has_pending_interrupts(access)) {
set_interrupt_limits(access);
}
}
void StackGuard::SetStackLimit(uintptr_t limit) {
ExecutionAccess access(isolate_);
// If the current limits are special (e.g. due to a pending interrupt) then
......@@ -365,6 +357,27 @@ void StackGuard::SetStackLimit(uintptr_t limit) {
}
void StackGuard::AdjustStackLimitForSimulator() {
ExecutionAccess access(isolate_);
uintptr_t climit = thread_local_.real_climit_;
// If the current limits are special (e.g. due to a pending interrupt) then
// leave them alone.
uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, climit);
if (thread_local_.jslimit() == thread_local_.real_jslimit_) {
thread_local_.set_jslimit(jslimit);
isolate_->heap()->SetStackLimits();
}
}
void StackGuard::EnableInterrupts() {
ExecutionAccess access(isolate_);
if (has_pending_interrupts(access)) {
set_interrupt_limits(access);
}
}
void StackGuard::DisableInterrupts() {
ExecutionAccess access(isolate_);
reset_limits(access);
......
......@@ -136,6 +136,11 @@ class StackGuard final {
// is assumed to grow downwards.
void SetStackLimit(uintptr_t limit);
// The simulator uses a separate JS stack. Limits on the JS stack might have
// to be adjusted in order to reflect overflows of the C stack, because we
// cannot rely on the interleaving of frames on the simulator.
void AdjustStackLimitForSimulator();
// Threading support.
char* ArchiveStackGuard(char* to);
char* RestoreStackGuard(char* from);
......
......@@ -1807,9 +1807,15 @@ void Simulator::WriteB(int32_t addr, int8_t value) {
// Returns the limit of the stack area to enable checking for stack overflows.
uintptr_t Simulator::StackLimit() const {
// Leave a safety margin of 1024 bytes to prevent overrunning the stack when
// pushing values.
uintptr_t Simulator::StackLimit(uintptr_t c_limit) const {
// The simulator uses a separate JS stack. If we have exhausted the C stack,
// we also drop down the JS limit to reflect the exhaustion on the JS stack.
if (GetCurrentStackPosition() < c_limit) {
return reinterpret_cast<uintptr_t>(get_sp());
}
// Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes
// to prevent overrunning the stack when pushing values.
return reinterpret_cast<uintptr_t>(stack_) + 1024;
}
......@@ -4464,6 +4470,9 @@ void Simulator::Execute() {
void Simulator::CallInternal(byte* entry) {
// Adjust JS-based stack limit to C-based stack limit.
isolate_->stack_guard()->AdjustStackLimitForSimulator();
// Prepare to execute the code at entry.
set_register(pc, reinterpret_cast<int32_t>(entry));
// Put down marker for end of simulation. The simulator will stop simulation
......
......@@ -193,12 +193,12 @@ class Simulator {
void set_pc(int32_t value);
int32_t get_pc() const;
Address get_sp() {
Address get_sp() const {
return reinterpret_cast<Address>(static_cast<intptr_t>(get_register(sp)));
}
// Accessor to the internal simulator stack area.
uintptr_t StackLimit() const;
uintptr_t StackLimit(uintptr_t c_limit) const;
// Executes MIPS instructions until the PC reaches end_sim_pc.
void Execute();
......@@ -466,15 +466,14 @@ class Simulator {
// The simulator has its own stack. Thus it has a different stack limit from
// the C-based native code. Setting the c_limit to indicate a very small
// stack cause stack overflow errors, since the simulator ignores the input.
// This is unlikely to be an issue in practice, though it might cause testing
// trouble down the line.
// the C-based native code. The JS-based limit normally points near the end of
// the simulator stack. When the C-based limit is exhausted we reflect that by
// lowering the JS-based limit as well, to make stack checks trigger.
class SimulatorStack : public v8::internal::AllStatic {
public:
static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
uintptr_t c_limit) {
return Simulator::current(isolate)->StackLimit();
return Simulator::current(isolate)->StackLimit(c_limit);
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
......
......@@ -1800,9 +1800,15 @@ void Simulator::WriteB(int64_t addr, int8_t value) {
// Returns the limit of the stack area to enable checking for stack overflows.
uintptr_t Simulator::StackLimit() const {
// Leave a safety margin of 1024 bytes to prevent overrunning the stack when
// pushing values.
uintptr_t Simulator::StackLimit(uintptr_t c_limit) const {
// The simulator uses a separate JS stack. If we have exhausted the C stack,
// we also drop down the JS limit to reflect the exhaustion on the JS stack.
if (GetCurrentStackPosition() < c_limit) {
return reinterpret_cast<uintptr_t>(get_sp());
}
// Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes
// to prevent overrunning the stack when pushing values.
return reinterpret_cast<uintptr_t>(stack_) + 1024;
}
......@@ -4620,6 +4626,9 @@ void Simulator::Execute() {
void Simulator::CallInternal(byte* entry) {
// Adjust JS-based stack limit to C-based stack limit.
isolate_->stack_guard()->AdjustStackLimitForSimulator();
// Prepare to execute the code at entry.
set_register(pc, reinterpret_cast<int64_t>(entry));
// Put down marker for end of simulation. The simulator will stop simulation
......
......@@ -223,12 +223,12 @@ class Simulator {
void set_pc(int64_t value);
int64_t get_pc() const;
Address get_sp() {
Address get_sp() const {
return reinterpret_cast<Address>(static_cast<intptr_t>(get_register(sp)));
}
// Accessor to the internal simulator stack area.
uintptr_t StackLimit() const;
uintptr_t StackLimit(uintptr_t c_limit) const;
// Executes MIPS instructions until the PC reaches end_sim_pc.
void Execute();
......@@ -509,15 +509,14 @@ class Simulator {
// The simulator has its own stack. Thus it has a different stack limit from
// the C-based native code. Setting the c_limit to indicate a very small
// stack cause stack overflow errors, since the simulator ignores the input.
// This is unlikely to be an issue in practice, though it might cause testing
// trouble down the line.
// the C-based native code. The JS-based limit normally points near the end of
// the simulator stack. When the C-based limit is exhausted we reflect that by
// lowering the JS-based limit as well, to make stack checks trigger.
class SimulatorStack : public v8::internal::AllStatic {
public:
static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
uintptr_t c_limit) {
return Simulator::current(isolate)->StackLimit();
return Simulator::current(isolate)->StackLimit(c_limit);
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
......
// Copyright 2015 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.
var global = this;
global.__defineSetter__('x', function(v) { x = v; });
assertThrows("global.x = 0", RangeError);
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