← All issues

[16] [JSC] JSLock m_hasOwnerThread has concurrency issue

Severity: Medium | Component: JavaScriptCore JSLock | aed1fdd

Rated Medium because the diff restores publication symmetry on JSLock ownership state. The race could cause currentThreadIsHoldingLock() to return true on a thread that did not hold the lock, allowing two threads to enter the VM concurrently — but reaching the racy observation requires a stale m_ownerThread that happens to equal the current thread, which is timing-dependent.

JSLock::lock published m_ownerThread then m_hasOwnerThread = true separated by a writer-side storeStoreFence(). Readers loaded both fields with plain non-atomic loads — no matching acquire barrier — so on weakly ordered hardware a reader could observe m_hasOwnerThread == true paired with a stale m_ownerThread.

Source/JavaScriptCore/runtime/JSLock.cpp / JSLock.h

m_ownerThread = &Thread::currentSingleton();
- WTF::storeStoreFence();
- m_hasOwnerThread = true;
+ m_hasOwnerThread.store(true, std::memory_order_release);
- bool currentThreadIsHoldingLock() { return m_hasOwnerThread && m_ownerThread.get() == &Thread::currentSingleton(); }
+ bool currentThreadIsHoldingLock() { return m_hasOwnerThread.load(std::memory_order_acquire) && m_ownerThread.get() == &Thread::currentSingleton(); }

m_hasOwnerThread becomes std::atomic<bool>. Writers use memory_order_release; readers in ownerThread(), ownerThreadUID(), and currentThreadIsHoldingLock() use memory_order_acquire.

Asymmetric memory fencing — release-side ordering on the writer paired with unordered, non-atomic loads on the reader breaks the publication invariant between two fields.

JSLock is recursive — lock() checks currentThreadIsHoldingLock() and bumps m_lockCount instead of taking the underlying mutex if true. The (m_hasOwnerThread, m_ownerThread) pair is a published racy-readable view used by SamplingProfiler, MachineThreads, and Web Thread interop without holding m_lock. Release-acquire pairing on the same atomic synchronizes prior writes with subsequent reads.

🔒

The memory-ordering and mutual-exclusion implications of this bug, and what an attacker could plausibly do with a one-sided publication on a recursive lock, are explored in depth.

Subscribe to read more

🔒

Multiple reusable audit patterns identified around one-sided fencing and lock-ownership publication, with concrete starting points across JSC and WTF.

Subscribe to read more