[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(); }
Patch Details
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.
Background
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.
Aa a Aaaaa Aaaaaaaaaaaaaaa Aaaaaaa Aa Aaaaa Aaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaa Aaaa Aaaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaa a Aaa Aaaaaaa Aa Aaa Aa Aaaaaaaaaaaaa Aaaa Aaa Aaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaa Aaaaa Aa Aaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaa Aaaaa Aaa Aaaa Aaaaaaaaa Aaaa Aaa Aaa Aaaa Aa Aaaaa Aaaaaaa Aaaaa a Aaaaaaaa Aaaaaaaaaaa Aaaaaaaaa
🔒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
Audit directions
a Aaaaaaaaaaa Aaaaaaa Aaaaa a Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaa Aaaaa Aaaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaa Aaa Aaaa Aaa Aaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaa Aa Aaa Aaaaaaaaa Aaaaa
a Aaaaaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaaa
a Aaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaa Aaaaa Aaaaaaa Aaaaa Aaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaaa Aaaaa Aaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa
a Aaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaa Aaaaa Aa Aaaa Aaaaaa Aaaaaaaaaaa Aaaa Aaaaaa Aaa Aaaaaa Aaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaaa
🔒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