Skip to content

Commit c65556c

Browse files
aamcommit-bot@chromium.org
authored andcommitted
[vm/unwind] Ensure no dart api calls happen after unwind error raised.
TEST=DartAPI_EnsureUnwindError* Change-Id: Ic14e806c34635954cb04a759f5915809dec1ad34 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/214762 Commit-Queue: Alexander Aprelev <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent 28b01e8 commit c65556c

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed

runtime/lib/isolate.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,6 @@ DEFINE_NATIVE_ENTRY(SendPortImpl_sendAndExitInternal_, 0, 2) {
302302
isolate->group()->api_state()->AllocatePersistentHandle();
303303
handle->set_ptr(msg_array);
304304
isolate->bequeath(std::unique_ptr<Bequest>(new Bequest(handle, port.Id())));
305-
// TODO(aam): Ensure there are no dart api calls after this point as we want
306-
// to ensure that validated message won't get tampered with.
307305
Isolate::KillIfExists(isolate, Isolate::LibMsgId::kKillMsg);
308306
// Drain interrupts before running so any IMMEDIATE operations on the current
309307
// isolate happen synchronously.

runtime/vm/dart_api_impl.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,16 @@ Dart_Handle Api::AcquiredError(IsolateGroup* isolate_group) {
485485
return reinterpret_cast<Dart_Handle>(acquired_error_handle);
486486
}
487487

488+
Dart_Handle Api::UnwindInProgressError() {
489+
Thread* T = Thread::Current();
490+
CHECK_API_SCOPE(T);
491+
TransitionToVM transition(T);
492+
HANDLESCOPE(T);
493+
const String& message = String::Handle(
494+
Z, String::New("No api calls are allowed while unwind is in progress"));
495+
return Api::NewHandle(T, UnwindError::New(message));
496+
}
497+
488498
bool Api::IsValid(Dart_Handle handle) {
489499
Isolate* isolate = Isolate::Current();
490500
Thread* thread = Thread::Current();

runtime/vm/dart_api_impl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ class Api : AllStatic {
194194
// Gets the handle which holds the pre-created acquired error object.
195195
static Dart_Handle AcquiredError(IsolateGroup* isolate_group);
196196

197+
// Gets the handle for unwind-is-in-progress error.
198+
static Dart_Handle UnwindInProgressError();
199+
197200
// Returns true if the handle holds a Smi.
198201
static bool IsSmi(Dart_Handle handle) {
199202
// Important: we do not require current thread to be in VM state because
@@ -335,6 +338,9 @@ class Api : AllStatic {
335338
if (thread->no_callback_scope_depth() != 0) { \
336339
return reinterpret_cast<Dart_Handle>( \
337340
Api::AcquiredError(thread->isolate_group())); \
341+
} \
342+
if (thread->is_unwind_in_progress()) { \
343+
return reinterpret_cast<Dart_Handle>(Api::UnwindInProgressError()); \
338344
}
339345

340346
#define ASSERT_CALLBACK_STATE(thread) \

runtime/vm/dart_api_impl_test.cc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,83 @@ TEST_CASE(DartAPI_UnhandleExceptionError) {
707707
EXPECT_STREQ(kRegularString, exception_cstr);
708708
}
709709

710+
void JustPropagateErrorNative(Dart_NativeArguments args) {
711+
Dart_Handle closure = Dart_GetNativeArgument(args, 0);
712+
EXPECT(Dart_IsClosure(closure));
713+
Dart_Handle result = Dart_InvokeClosure(closure, 0, NULL);
714+
EXPECT(Dart_IsError(result));
715+
Dart_PropagateError(result);
716+
UNREACHABLE();
717+
}
718+
719+
static Dart_NativeFunction JustPropagateError_lookup(Dart_Handle name,
720+
int argument_count,
721+
bool* auto_setup_scope) {
722+
ASSERT(auto_setup_scope != NULL);
723+
*auto_setup_scope = true;
724+
return JustPropagateErrorNative;
725+
}
726+
727+
TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenKilled) {
728+
const char* kScriptChars = R"(
729+
import 'dart:isolate';
730+
731+
exitRightNow() {
732+
Isolate.current.kill(priority: Isolate.immediate);
733+
}
734+
735+
@pragma("vm:external-name", "Test_nativeFunc")
736+
external void nativeFunc(closure);
737+
738+
void Func1() {
739+
nativeFunc(() => exitRightNow());
740+
}
741+
)";
742+
Dart_Handle lib =
743+
TestCase::LoadTestScript(kScriptChars, &JustPropagateError_lookup);
744+
Dart_Handle result;
745+
746+
result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
747+
EXPECT(Dart_IsError(result));
748+
EXPECT_SUBSTRING("isolate terminated by Isolate.kill", Dart_GetError(result));
749+
750+
result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
751+
EXPECT(Dart_IsError(result));
752+
EXPECT_SUBSTRING("No api calls are allowed while unwind is in progress",
753+
Dart_GetError(result));
754+
}
755+
756+
TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenSendAndExit) {
757+
const char* kScriptChars = R"(
758+
import 'dart:isolate';
759+
import 'dart:_internal' show sendAndExit;
760+
761+
sendAndExitNow() {
762+
final receivePort = ReceivePort();
763+
sendAndExit(receivePort.sendPort, true);
764+
}
765+
766+
@pragma("vm:external-name", "Test_nativeFunc")
767+
external void nativeFunc(closure);
768+
769+
void Func1() {
770+
nativeFunc(() => sendAndExitNow());
771+
}
772+
)";
773+
Dart_Handle lib =
774+
TestCase::LoadTestScript(kScriptChars, &JustPropagateError_lookup);
775+
Dart_Handle result;
776+
777+
result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
778+
EXPECT(Dart_IsError(result));
779+
EXPECT_SUBSTRING("isolate terminated by Isolate.kill", Dart_GetError(result));
780+
781+
result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
782+
EXPECT(Dart_IsError(result));
783+
EXPECT_SUBSTRING("No api calls are allowed while unwind is in progress",
784+
Dart_GetError(result));
785+
}
786+
710787
// Should we propagate the error via Dart_SetReturnValue?
711788
static bool use_set_return = false;
712789

runtime/vm/isolate.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,7 @@ ErrorPtr IsolateMessageHandler::HandleLibMessage(const Array& message) {
11261126
if (!obj.IsSmi()) return Error::null();
11271127
const intptr_t priority = Smi::Cast(obj).Value();
11281128
if (priority == Isolate::kImmediateAction) {
1129+
Thread::Current()->StartUnwindError();
11291130
obj = message.At(2);
11301131
if (I->VerifyTerminateCapability(obj)) {
11311132
// We will kill the current isolate by returning an UnwindError.

runtime/vm/runtime_entry.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3635,6 +3635,9 @@ static Thread* GetThreadForNativeCallback(uword callback_id,
36353635
if (thread->no_callback_scope_depth() != 0) {
36363636
FATAL("Cannot invoke native callback when API callbacks are prohibited.");
36373637
}
3638+
if (thread->is_unwind_in_progress()) {
3639+
FATAL("Cannot invoke native callback while unwind error propagates.");
3640+
}
36383641
if (!thread->IsMutatorThread()) {
36393642
FATAL("Native callbacks must be invoked on the mutator thread.");
36403643
}

runtime/vm/thread.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@ class Thread : public ThreadState {
521521
no_callback_scope_depth_ -= 1;
522522
}
523523

524+
bool is_unwind_in_progress() const { return is_unwind_in_progress_; }
525+
526+
void StartUnwindError() { is_unwind_in_progress_ = true; }
527+
524528
#if defined(DEBUG)
525529
void EnterCompiler() {
526530
ASSERT(!IsInsideCompiler());
@@ -1182,6 +1186,8 @@ class Thread : public ThreadState {
11821186
Thread* next_; // Used to chain the thread structures in an isolate.
11831187
bool is_mutator_thread_ = false;
11841188

1189+
bool is_unwind_in_progress_ = false;
1190+
11851191
#if defined(DEBUG)
11861192
bool inside_compiler_ = false;
11871193
#endif

0 commit comments

Comments
 (0)