From 812696e563b737453984be683f36d1a75ac99b51 Mon Sep 17 00:00:00 2001 From: Adrian Niculescu <15037449+adrian-niculescu@users.noreply.github.com> Date: Tue, 13 Oct 2026 22:29:57 +0300 Subject: [PATCH] fix(runtime): publish an isolate's Caches under the registry lock Runtime::NotifyMemoryPressure runs on a global queue and, holding the isolate registry lock, reads the Caches slot of every registered isolate to get its gate id. CreateIsolate registers the isolate, but Runtime::Init stored the slot afterwards without the lock. A memory pressure event that arrives while a main or worker isolate is starting therefore reads the slot on one thread while the starting thread writes it. A reader that finds the slot empty gets the stand-in cache, whose gate id matches no gate, and skips the isolate, so the practical outcome is benign. What is left is an unsynchronized read of a pointer while it is published. Init now stores the slot under the registry lock. Teardown already removes the isolate from the registry under that lock before Caches::Remove clears the slot. --- NativeScript/runtime/Runtime.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/NativeScript/runtime/Runtime.mm b/NativeScript/runtime/Runtime.mm index 3e15883f..67c429a5 100644 --- a/NativeScript/runtime/Runtime.mm +++ b/NativeScript/runtime/Runtime.mm @@ -393,8 +393,14 @@ void DisposeIsolateWhenPossible(Isolate* isolate, int gateId) { } void Runtime::Init(Isolate* isolate, bool isWorker) { - std::shared_ptr cache = - Caches::Init(isolate, nextIsolateId.fetch_add(1, std::memory_order_relaxed)); + std::shared_ptr cache; + { + // CreateIsolate has already registered the isolate, so NotifyMemoryPressure + // can read its Caches slot from another thread. It reads under the registry + // lock, so the slot is published under it too. + SpinLock lock(isolatesMutex_); + cache = Caches::Init(isolate, nextIsolateId.fetch_add(1, std::memory_order_relaxed)); + } // Before anything can build an IsolateWrapper for this isolate. IsolateGates::Open(cache->getGateId()); cache->isWorker = isWorker;