Skip to content

fix(runtime): re-check isolate liveness under the Locker in ObjC-to-JS callbacks - #475

Merged
NathanWalker merged 1 commit into
mainfrom
fix/objc-callback-dead-isolate
Sep 11, 2026
Merged

NathanWalker merged 1 commit into
mainfrom
fix/objc-callback-dead-isolate

Conversation

@edusperoni

Copy link
Copy Markdown
Collaborator

An Objective-C callback implemented in JS (an exposed method on an extended class, or a JS block) can be invoked on any thread while the isolate that owns it is being torn down. Both entry points checked the isolate was alive before taking its Locker, but ~Runtime holds that lock while it removes the isolate's caches and clears the context. The pre-lock check could therefore pass, the lock be granted only after the caches were gone, and the callback continue with the dummy cache Caches::Get hands out for a disposed isolate, whose context persistent is empty: Caches::GetContext then dereferences null.

Seen on the shared spec Should not crash if the worker registers a notification, where the worker registers an NSNotificationCenter observer, the parent terminates it and posts the notification straight away: the main thread's post raced the worker thread's ~Runtime, and the crash report shows the observer's MethodCallback on the main thread with the worker in DisposeIsolateWhenPossible.

Both ArgConverter::MethodCallback and the JSBlock dispose helper now re-check IsolateWrapper::IsValid() once the Locker is held. ~Runtime erases the isolate from the live registry before it takes the lock, which is what makes the second look reliable; a caller that lost the race returns a zeroed result (or skips the persistent reset) instead of touching the dead isolate.

One window stays open and is out of scope here: a Locker taken on an isolate between the destructor's unlock and Isolate::Dispose. Closing it needs the registry mutex held across the check and the lock acquisition.

Suite: 1543 / 0 locally on top of main (19b880c).

…S callbacks

Both callback entry points validated the isolate before taking its Locker,
but ~Runtime holds that lock while it removes the caches and clears the
context, so a callback arriving during a worker's teardown could pass the
check, wait on the lock, and then continue with the dummy cache a disposed
isolate hands out, whose context persistent is empty. The check now runs
again once the lock is held; ~Runtime drops the isolate from the live
registry before taking the lock, which makes the second look reliable.
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 49 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 389809c7-262a-4e42-93ba-ca5659091a68

📥 Commits

Reviewing files that changed from the base of the PR and between 19b880c and 15b06cc.

📒 Files selected for processing (2)
  • NativeScript/runtime/ArgConverter.mm
  • NativeScript/runtime/Interop.mm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@NathanWalker
NathanWalker merged commit 6221ca8 into main Sep 11, 2026
9 checks passed
@NathanWalker
NathanWalker deleted the fix/objc-callback-dead-isolate branch September 11, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants