[vm] Fix crash when enum initialization hits a stack overflow.

Give the compiler more headroom.

Have the simulators and interpreters distinguish between the true stack limit and the limit for trigger an overflow exception.

Bug: https://github.com/dart-lang/sdk/issues/35224
Bug: https://github.com/flutter/flutter/issues/25041
Change-Id: I048d73b649b4ce1f3d9b5a33d898ee9cfe54f6ca
Reviewed-on: https://dart-review.googlesource.com/c/86821
Reviewed-by: Aart Bik <ajcbik@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak
2018-12-10 23:40:21 +00:00
committed by commit-bot@chromium.org
parent 81de5ec21c
commit 30896b87f1
14 changed files with 77 additions and 23 deletions
+7 -6
View File
@@ -2228,7 +2228,7 @@ void ClassFinalizer::AllocateEnumValues(const Class& enum_cls) {
sentinel.RecordStore(enum_value);
if (enum_cls.kernel_offset() > 0) {
Object& result = Object::Handle(zone);
Error& error = Error::Handle(zone);
for (intptr_t i = 0; i < fields.Length(); i++) {
field = Field::RawCast(fields.At(i));
if (!field.is_static() || !field.is_const() ||
@@ -2238,11 +2238,12 @@ void ClassFinalizer::AllocateEnumValues(const Class& enum_cls) {
// The eager evaluation of the enum values is required for hot-reload (see
// commit e3ecc87).
if (!FLAG_precompiled_mode) {
field.SetStaticValue(Object::transition_sentinel());
result = Compiler::EvaluateStaticInitializer(field);
ASSERT(!result.IsError());
field.SetStaticValue(Instance::Cast(result), true);
field.RecordStore(Instance::Cast(result));
if (field.IsUninitialized()) {
error = field.EvaluateInitializer();
if (!error.IsNull()) {
ReportError(error);
}
}
}
}
} else {
+2 -2
View File
@@ -59,9 +59,9 @@ class ScopedIsolateStackLimits : public ValueObject {
ASSERT(thread->isolate() == Isolate::Current());
saved_stack_limit_ = thread->saved_stack_limit();
#if defined(USING_SIMULATOR)
thread->SetStackLimit(Simulator::Current()->stack_limit());
thread->SetStackLimit(Simulator::Current()->overflow_stack_limit());
#else
thread->SetStackLimit(OSThread::Current()->stack_limit_with_headroom());
thread->SetStackLimit(OSThread::Current()->overflow_stack_limit());
// TODO(regis): For now, the interpreter is using its own stack limit.
#endif
+6 -4
View File
@@ -559,8 +559,10 @@ Interpreter::Interpreter()
// Low address.
stack_base_ =
reinterpret_cast<uword>(stack_) + kInterpreterStackUnderflowSize;
// Limit for StackOverflowError.
overflow_stack_limit_ = stack_base_ + OSThread::GetSpecifiedStackSize();
// High address.
stack_limit_ = stack_base_ + OSThread::GetSpecifiedStackSize();
stack_limit_ = overflow_stack_limit_ + OSThread::kStackSizeBuffer;
last_setjmp_buffer_ = NULL;
@@ -1383,7 +1385,7 @@ DART_FORCE_INLINE bool Interpreter::InstanceCall2(Thread* thread,
reinterpret_cast<uword>(this), reinterpret_cast<uword>(fp_), \
exit_fp); \
} \
ASSERT(reinterpret_cast<uword>(fp_) < stack_limit()); \
ASSERT(HasFrame(reinterpret_cast<uword>(fp_))); \
return special_[KernelBytecode::kExceptionSpecialIndex]; \
} \
goto DispatchAfterException; \
@@ -1889,7 +1891,7 @@ SwitchDispatch:
{
// Check the interpreter's own stack limit for actual interpreter's stack
// overflows, and also the thread's stack limit for scheduled interrupts.
if (reinterpret_cast<uword>(SP) >= stack_limit() ||
if (reinterpret_cast<uword>(SP) >= overflow_stack_limit() ||
thread->HasScheduledInterrupts()) {
Exit(thread, FP, SP + 1, pc);
NativeArguments args(thread, 0, NULL, NULL);
@@ -2352,7 +2354,7 @@ SwitchDispatch:
reinterpret_cast<uword>(this), reinterpret_cast<uword>(fp_),
exit_fp);
}
ASSERT(reinterpret_cast<uword>(fp_) < stack_limit());
ASSERT(HasFrame(reinterpret_cast<uword>(fp_)));
const intptr_t argc = reinterpret_cast<uword>(pc) >> 2;
ASSERT(fp_ == FrameArguments(FP, argc + kKBCEntrySavedSlots));
// Exception propagation should have been done.
+4 -1
View File
@@ -49,6 +49,8 @@ class Interpreter {
// Low address (KBC stack grows up).
uword stack_base() const { return stack_base_; }
// Limit for StackOverflowError.
uword overflow_stack_limit() const { return overflow_stack_limit_; }
// High address (KBC stack grows up).
uword stack_limit() const { return stack_limit_; }
@@ -56,7 +58,7 @@ class Interpreter {
// TODO(regis): We should rely on a new thread vm_tag to identify an
// interpreter frame and not need this HasFrame() method.
bool HasFrame(uword frame) const {
return frame >= stack_base() && frame <= get_fp();
return frame >= stack_base() && frame < stack_limit();
}
// Identify an entry frame by looking at its pc marker value.
@@ -99,6 +101,7 @@ class Interpreter {
private:
uintptr_t* stack_;
uword stack_base_;
uword overflow_stack_limit_;
uword stack_limit_;
RawObject** fp_;
+2 -4
View File
@@ -100,9 +100,7 @@ class OSThread : public BaseThread {
uword stack_base() const { return stack_base_; }
uword stack_limit() const { return stack_limit_; }
uword stack_limit_with_headroom() const {
return stack_limit_ + kStackSizeBuffer;
}
uword overflow_stack_limit() const { return stack_limit_ + kStackSizeBuffer; }
bool HasStackHeadroom(intptr_t headroom = kStackSizeBuffer) {
return GetCurrentStackPointer() > (stack_limit_ + headroom);
@@ -211,7 +209,7 @@ class OSThread : public BaseThread {
static void DisableOSThreadCreation();
static void EnableOSThreadCreation();
static const intptr_t kStackSizeBuffer = (4 * KB * kWordSize);
static const intptr_t kStackSizeBuffer = (16 * KB * kWordSize);
static const ThreadId kInvalidThreadId;
static const ThreadJoinId kInvalidThreadJoinId;
+1 -1
View File
@@ -1981,7 +1981,7 @@ DEFINE_RUNTIME_ENTRY(StackOverflow, 0) {
Interpreter* interpreter = Thread::Current()->interpreter();
if (interpreter != NULL) {
interpreter_stack_overflow =
interpreter->get_sp() >= interpreter->stack_limit();
interpreter->get_sp() >= interpreter->overflow_stack_limit();
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+4 -2
View File
@@ -673,9 +673,11 @@ Simulator::Simulator() : exclusive_access_addr_(0), exclusive_access_value_(0) {
new char[(OSThread::GetSpecifiedStackSize() + OSThread::kStackSizeBuffer +
kSimulatorStackUnderflowSize)];
// Low address.
stack_limit_ = reinterpret_cast<uword>(stack_) + OSThread::kStackSizeBuffer;
stack_limit_ = reinterpret_cast<uword>(stack_);
// Limit for StackOverflowError.
overflow_stack_limit_ = stack_limit_ + OSThread::kStackSizeBuffer;
// High address.
stack_base_ = stack_limit_ + OSThread::GetSpecifiedStackSize();
stack_base_ = overflow_stack_limit_ + OSThread::GetSpecifiedStackSize();
pc_modified_ = false;
icount_ = 0;
+3
View File
@@ -79,6 +79,8 @@ class Simulator {
// High address.
uword stack_base() const { return stack_base_; }
// Limit for StackOverflowError.
uword overflow_stack_limit() const { return overflow_stack_limit_; }
// Low address.
uword stack_limit() const { return stack_limit_; }
@@ -149,6 +151,7 @@ class Simulator {
// Simulator support.
char* stack_;
uword stack_limit_;
uword overflow_stack_limit_;
uword stack_base_;
bool pc_modified_;
uint64_t icount_;
+4 -2
View File
@@ -723,9 +723,11 @@ Simulator::Simulator() : exclusive_access_addr_(0), exclusive_access_value_(0) {
new char[(OSThread::GetSpecifiedStackSize() + OSThread::kStackSizeBuffer +
kSimulatorStackUnderflowSize)];
// Low address.
stack_limit_ = reinterpret_cast<uword>(stack_) + OSThread::kStackSizeBuffer;
stack_limit_ = reinterpret_cast<uword>(stack_);
// Limit for StackOverflowError.
overflow_stack_limit_ = stack_limit_ + OSThread::kStackSizeBuffer;
// High address.
stack_base_ = stack_limit_ + OSThread::GetSpecifiedStackSize();
stack_base_ = overflow_stack_limit_ + OSThread::GetSpecifiedStackSize();
pc_modified_ = false;
icount_ = 0;
+3
View File
@@ -75,6 +75,8 @@ class Simulator {
// High address.
uword stack_base() const { return stack_base_; }
// Limit for StackOverflowError.
uword overflow_stack_limit() const { return overflow_stack_limit_; }
// Low address.
uword stack_limit() const { return stack_limit_; }
@@ -136,6 +138,7 @@ class Simulator {
int64_t pc_;
char* stack_;
uword stack_limit_;
uword overflow_stack_limit_;
uword stack_base_;
bool pc_modified_;
uint64_t icount_;
+3 -1
View File
@@ -557,8 +557,10 @@ Simulator::Simulator() : stack_(NULL), fp_(NULL), pp_(NULL), argdesc_(NULL) {
sizeof(uintptr_t)];
// Low address.
stack_base_ = reinterpret_cast<uword>(stack_) + kSimulatorStackUnderflowSize;
// Limit for StackOverflowError.
overflow_stack_limit_ = stack_base_ + OSThread::GetSpecifiedStackSize();
// High address.
stack_limit_ = stack_base_ + OSThread::GetSpecifiedStackSize();
stack_limit_ = overflow_stack_limit_ + OSThread::kStackSizeBuffer;
last_setjmp_buffer_ = NULL;
+3
View File
@@ -49,6 +49,8 @@ class Simulator {
// Low address (DBC stack grows up).
uword stack_base() const { return stack_base_; }
// Limit for StackOverflowError.
uword overflow_stack_limit() const { return overflow_stack_limit_; }
// High address (DBC stack grows up).
uword stack_limit() const { return stack_limit_; }
@@ -89,6 +91,7 @@ class Simulator {
private:
uintptr_t* stack_;
uword stack_base_;
uword overflow_stack_limit_;
uword stack_limit_;
RawObject** fp_;
@@ -0,0 +1,33 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// https://github.com/flutter/flutter/issues/25041
// This test may produce a compile time exception from stack overflow during
// enum initialization or succeed in enum initialization depending on exactly
// how much stack is left and used by the compiler. It should never crash nor
// produce a runtime exception.
enum Fruit {
apple,
banana,
}
getFruit() => Fruit.apple;
recurse() {
try {
recurse();
} catch (e, st) {
print("$e ${getFruit()}");
}
}
main() {
try {
recurse();
} on StackOverflowError catch (e) {
// Swallow.
}
}
@@ -4,6 +4,8 @@
# Sections in this file should contain "$compiler == dartk" or
# "$compiler == dartkp".
enum_initialization_near_stack_overflow_test: Pass, CompileTimeError, OK
[ $compiler == app_jitk ]
assertion_initializer_const_error2_test/cc01: MissingCompileTimeError
assertion_initializer_const_error2_test/cc02: MissingCompileTimeError