[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:
committed by
commit-bot@chromium.org
parent
81de5ec21c
commit
30896b87f1
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user