@@ -4962,85 +4962,58 @@ DEFINE_LEAF_RUNTIME_ENTRY(ExitSafepoint,
49624962 /* argument_count=*/ 0 ,
49634963 DLRT_ExitSafepoint);
49644964
4965- // This is called by a native callback trampoline
4966- // (see StubCodeCompiler::GenerateFfiCallbackTrampolineStub). Not registered as
4967- // a runtime entry because we can't use Thread to look it up.
4968- extern " C" Thread* DLRT_GetFfiCallbackMetadata (
4969- FfiCallbackMetadata::Trampoline trampoline,
4970- uword* out_entry_point,
4971- uword* out_trampoline_type) {
4972- CHECK_STACK_ALIGNMENT;
4973- TRACE_RUNTIME_CALL (" GetFfiCallbackMetadata %p" ,
4974- reinterpret_cast <void *>(trampoline));
4975- ASSERT (out_entry_point != nullptr );
4976- ASSERT (out_trampoline_type != nullptr );
4977-
4978- if (!Isolate::IsolateCreationEnabled ()) {
4979- TRACE_RUNTIME_CALL (" GetFfiCallbackMetadata called after shutdown %p" ,
4980- reinterpret_cast <void *>(trampoline));
4981- return nullptr ;
4982- }
4983-
4965+ namespace {
4966+ Thread* HandleAsyncFfiCallback (FfiCallbackMetadata::Metadata metadata,
4967+ uword* out_entry_point,
4968+ uword* out_trampoline_type) {
4969+ // NOTE: This is only thread safe if the user is using the API correctly.
4970+ // Otherwise, the callback could have been deleted and replaced, in which case
4971+ // IsLive would still be true. Or it could have been deleted after we looked
4972+ // it up, and the target isolate could be shut down. We delay recycling
4973+ // callbacks as long as possible, so this check is better than nothing, but
4974+ // it's not infallible. Ultimately it's the user's responsibility to avoid use
4975+ // after free errors. Trying to lock FfiCallbackMetadata::lock_, or any
4976+ // similar lock, leads to deadlocks.
4977+
4978+ *out_trampoline_type = static_cast <uword>(metadata.trampoline_type ());
4979+ *out_entry_point = metadata.target_entry_point ();
4980+ Isolate* target_isolate = metadata.target_isolate ();
4981+
4982+ Isolate* current_isolate = nullptr ;
49844983 Thread* current_thread = Thread::Current ();
4985- auto * fcm = FfiCallbackMetadata::Instance ();
4986- auto metadata = fcm->LookupMetadataForTrampoline (trampoline);
4987-
4988- // Is this an async callback?
4989- if (metadata.trampoline_type () ==
4990- FfiCallbackMetadata::TrampolineType::kAsync ) {
4991- // It's possible that the callback was deleted, or the target isolate was
4992- // shut down, in between looking up the metadata above, and this point. So
4993- // grab the lock and then check that the callback is still alive.
4994- MutexLocker locker (fcm->lock ());
4995- auto metadata2 = fcm->LookupMetadataForTrampoline (trampoline);
4996- *out_trampoline_type = static_cast <uword>(metadata2.trampoline_type ());
4997-
4998- // Check IsLive, but also check that the metdata hasn't changed. This is
4999- // for the edge case that the callback was destroyed and recycled in between
5000- // the two lookups.
5001- if (!metadata.IsLive () || !metadata.IsSameCallback (metadata2)) {
5002- TRACE_RUNTIME_CALL (" GetFfiCallbackMetadata callback deleted %p" ,
5003- reinterpret_cast <void *>(trampoline));
5004- return nullptr ;
5005- }
5006-
5007- *out_entry_point = metadata.target_entry_point ();
5008- Isolate* target_isolate = metadata.target_isolate ();
5009-
5010- Isolate* current_isolate = nullptr ;
5011- if (current_thread != nullptr ) {
5012- current_isolate = current_thread->isolate ();
5013- if (current_thread->execution_state () != Thread::kThreadInNative ) {
5014- FATAL (" Cannot invoke native callback from a leaf call." );
5015- }
5016- current_thread->ExitSafepointFromNative ();
5017- current_thread->set_execution_state (Thread::kThreadInVM );
4984+ if (current_thread != nullptr ) {
4985+ current_isolate = current_thread->isolate ();
4986+ if (current_thread->execution_state () != Thread::kThreadInNative ) {
4987+ FATAL (" Cannot invoke native callback from a leaf call." );
50184988 }
4989+ current_thread->ExitSafepointFromNative ();
4990+ current_thread->set_execution_state (Thread::kThreadInVM );
4991+ }
50194992
5020- // Enter the temporary isolate. If the current isolate is in the same group
5021- // as the target isolate, we can skip entering the temp isolate, and marshal
5022- // the args on the current isolate.
5023- if (current_isolate == nullptr ||
5024- current_isolate->group () != target_isolate->group ()) {
5025- if (current_isolate != nullptr ) {
5026- Thread::ExitIsolate (/* isolate_shutdown=*/ false );
5027- }
5028- target_isolate->group ()->EnterTemporaryIsolate ();
4993+ // Enter the temporary isolate. If the current isolate is in the same group
4994+ // as the target isolate, we can skip entering the temp isolate, and marshal
4995+ // the args on the current isolate.
4996+ if (current_isolate == nullptr ||
4997+ current_isolate->group () != target_isolate->group ()) {
4998+ if (current_isolate != nullptr ) {
4999+ Thread::ExitIsolate (/* isolate_shutdown=*/ false );
50295000 }
5030- Thread* const temp_thread = Thread::Current ();
5031- ASSERT (temp_thread != nullptr );
5032- temp_thread->set_unboxed_int64_runtime_arg (metadata.send_port ());
5033- temp_thread->set_unboxed_int64_runtime_second_arg (
5034- reinterpret_cast <intptr_t >(current_isolate));
5035- ASSERT (!temp_thread->IsAtSafepoint ());
5036- return temp_thread;
5001+ target_isolate->group ()->EnterTemporaryIsolate ();
50375002 }
5003+ Thread* const temp_thread = Thread::Current ();
5004+ ASSERT (temp_thread != nullptr );
5005+ temp_thread->set_unboxed_int64_runtime_arg (metadata.send_port ());
5006+ temp_thread->set_unboxed_int64_runtime_second_arg (
5007+ reinterpret_cast <intptr_t >(current_isolate));
5008+ ASSERT (!temp_thread->IsAtSafepoint ());
5009+ return temp_thread;
5010+ }
5011+
5012+ Thread* HandleSyncFfiCallback (FfiCallbackMetadata::Metadata metadata,
5013+ uword* out_entry_point,
5014+ uword* out_trampoline_type) {
5015+ Thread* current_thread = Thread::Current ();
50385016
5039- // Otherwise, this is a sync callback, so verify that we're already entered
5040- // into the target isolate.
5041- if (!metadata.IsLive ()) {
5042- FATAL (" Callback invoked after it has been deleted." );
5043- }
50445017 if (metadata.is_isolate_group_bound ()) {
50455018 *out_entry_point = metadata.target_entry_point ();
50465019 *out_trampoline_type = static_cast <uword>(metadata.trampoline_type ());
@@ -5102,6 +5075,53 @@ extern "C" Thread* DLRT_GetFfiCallbackMetadata(
51025075 (void *)*out_trampoline_type);
51035076 return current_thread;
51045077}
5078+ } // namespace
5079+
5080+ // This is called by a native callback trampoline
5081+ // (see StubCodeCompiler::GenerateFfiCallbackTrampolineStub). Not registered as
5082+ // a runtime entry because we can't use Thread to look it up.
5083+ extern " C" Thread* DLRT_GetFfiCallbackMetadata (
5084+ FfiCallbackMetadata::Trampoline trampoline,
5085+ uword* out_entry_point,
5086+ uword* out_trampoline_type) {
5087+ CHECK_STACK_ALIGNMENT;
5088+ TRACE_RUNTIME_CALL (" GetFfiCallbackMetadata %p" ,
5089+ reinterpret_cast <void *>(trampoline));
5090+ ASSERT (out_entry_point != nullptr );
5091+ ASSERT (out_trampoline_type != nullptr );
5092+
5093+ if (!Isolate::IsolateCreationEnabled ()) {
5094+ FATAL (" GetFfiCallbackMetadata called after shutdown %p" ,
5095+ reinterpret_cast <void *>(trampoline));
5096+ }
5097+
5098+ // NOTE: We access the metadata for `trampoline` without a lock. This is safe
5099+ // because nobody will touch the metadata of the `trampoline` until it's
5100+ // deleted and the `NativeCallable` API requires the isolate to keep the
5101+ // trampoline (and therefore the metadata) alive until C code no longer
5102+ // attempts to call it.
5103+ //
5104+ // If a user of the `NativeCallable` API violates this agreement, we may
5105+ // have a use-after-free scenario here and therefore undefined behavior.
5106+ // We make some best effort to `FATAL()` in obvious cases of undefined
5107+ // behavior, but not all cases will be caught.
5108+ auto metadata =
5109+ FfiCallbackMetadata::Instance ()->LookupMetadataForTrampolineUnlocked (
5110+ trampoline);
5111+
5112+ if (!metadata.IsLive ()) {
5113+ FATAL (" Callback invoked after it has been deleted." );
5114+ }
5115+
5116+ if (metadata.trampoline_type () ==
5117+ FfiCallbackMetadata::TrampolineType::kAsync ) {
5118+ return HandleAsyncFfiCallback (metadata, out_entry_point,
5119+ out_trampoline_type);
5120+ } else {
5121+ return HandleSyncFfiCallback (metadata, out_entry_point,
5122+ out_trampoline_type);
5123+ }
5124+ }
51055125
51065126extern " C" void DLRT_ExitIsolateGroupBoundIsolate () {
51075127 TRACE_RUNTIME_CALL (" ExitIsolateGroupBoundIsolate%s" , " " );
0 commit comments