[16] [JSC] JSLock m_hasOwnerThread has concurrency issue
Severity: Medium | Component: JavaScriptCore JSLock | aed1fdd
JSLock ownership state의 publication symmetry가 복원되는 패치이므로 Medium으로 평가됩니다. 이 race는 lock을 보유하지 않은 thread에서 currentThreadIsHoldingLock()이 true를 반환하게 만들어, 두 thread가 VM에 동시에 진입하는 상황을 유발할 수 있습니다. 다만 racy observation에 도달하려면, stale m_ownerThread가 우연히 현재 thread와 일치하는 타이밍이 필요합니다.
JSLock::lock은 m_ownerThread를 기록한 뒤 m_hasOwnerThread = true를 설정했으며, 두 쓰기 사이에는 writer 측의 storeStoreFence()가 있었습니다. 그러나 reader는 두 필드를 plain non-atomic load로 읽었고, 이에 대응하는 acquire barrier가 없었습니다. 결과적으로 weak ordering을 허용하는 하드웨어에서는 m_hasOwnerThread == true를 관찰하면서 동시에 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가 std::atomic<bool>로 변경되었습니다. Writer는 memory_order_release를 사용하고, ownerThread(), ownerThreadUID(), currentThreadIsHoldingLock()의 reader는 memory_order_acquire를 사용합니다.
비대칭 memory fencing — writer에는 release 순서가 적용되었지만 reader가 unordered non-atomic load를 사용하는 구조에서는, 두 필드 사이의 publication invariant가 깨집니다.
Background
JSLock은 재귀적으로 동작합니다. lock()은 currentThreadIsHoldingLock()을 확인하여 true이면 underlying mutex를 획득하는 대신 m_lockCount를 증가시킵니다. (m_hasOwnerThread, m_ownerThread) 쌍은 m_lock을 보유하지 않은 상태에서 SamplingProfiler, MachineThreads, Web Thread interop이 접근하는 racy-readable view로 공개됩니다. 동일한 atomic에 대한 release-acquire 쌍은 이전 write를 이후 read와 동기화합니다.
Aaaaa Aaaaaaaaaaaaaaaa Aaa Aa Aaaaaaa Aaaa Aaa Aa Aaaa Aaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaa Aaaaaa a Aa a Aaaaaaa Aaa Aaa Aaaa Aaa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaa a Aaaaa
a Aaaaaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaa Aaa Aa Aaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaa Aa Aaaa Aaa Aaaaa Aa Aaaaaaa Aaaaa Aaaa Aaaaaa a Aa Aaa Aaa Aaaaaa
🔒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.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaa Aaaaa Aaaaa Aaaa Aaa Aaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaaa a Aaa Aa Aaa Aaaaa Aaaaaa a Aaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaa Aaaa Aaa Aaaa Aaaa
a Aaaaaaaaaaaaaaa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaa Aaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaaa
a Aaaaaaaaaaaa Aaaaa Aa Aaaaa Aaaaaa Aa Aaaaaaaaaaaaaa Aaa Aaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aa Aa Aaa Aaaa Aaaa
a Aaaaaaaaa a Aaa Aaaaaa Aaaa Aaaaa Aaaaaa Aaaaa Aaaaaa Aaaa Aaaaaaaaa Aaaaaaaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaa Aaaa Aaaa Aa Aaa Aaaa Aaaa
🔒Multiple reusable audit patterns identified around one-sided fencing and lock-ownership publication, with concrete starting points across JSC and WTF.
더 확인하려면 구독해 주세요