[JSC][WASM][Debugger] Fix STW deadlocks when VM blocks in memory.atomic.wait or WebCore operations
Source/WebCore/workers/WorkerSTWParticipation.h
Source/JavaScriptCore/runtime/WaiterListManager.cpp
WASM debugger는 Stop-The-World(STW) 프로토콜을 통해 모든 VM을 중단시킵니다. 전역 NeedStopTheWorld 플래그가 설정되면, debugger 스레드는 각 VM이 notifyVMStop()을 호출하여 active count를 감소시킬 때까지 대기합니다. JSC는 일반적으로 interpreter loop 안의 trap check point를 통해 이 과정에 참여하게 됩니다.
그런데 memory.atomic.wait 또는 동기 WebCore 작업 내부에서 블로킹된 worker 스레드는 trap check point에 도달하지 못합니다. WebCore 작업은 내부적으로 BinarySemaphore::wait()를 통해 자체 run loop를 구동하기 때문입니다. 결과적으로 이들 스레드는 notifyVMStop()을 호출하지 않고, STW count는 0이 되지 않으며, debugger는 무한히 중단 상태에 빠지게 됩니다.
이 commit은 각 블로킹 지점에 polling 기반의 STW 참여 방식을 도입합니다. 먼저 신규 헬퍼 waitWithSTWParticipation()을 추가하여, 블로킹 중인 지점도 STW에 참여할 수 있도록 했습니다. 아울러 WaiterListManager::waitForSync()도 수정되었습니다. 수정 후에는 50ms 간격으로 polling하며, NeedStopTheWorld가 설정된 경우 notifyVMStop()을 호출합니다. 한편 새로 도입된 WasmAtomicsWaitBlocked callback 타입은 check-in마다 stop state를 초기화하지 않고, 여러 STW 사이클에 걸쳐 상태를 유지합니다. atomics-wait 지점은 대기가 완료되기 전까지 여러 STW 사이클에 걸쳐 진입될 수 있기 때문입니다.
Significance
WasmAtomicsWaitBlocked callback은 의도적으로 clearStop()을 건너뜁니다. 이로 인해 stop data는 여러 STW 사이클에 걸쳐 유지됩니다. 종료 경로에서 보상 역할의 clearStop()이 호출되지 않으면, debugger는 실행 중인 스레드에 대해 stale한 PC/CFR/stack 데이터를 참조하게 됩니다.
Audit directions
WasmAtomicsWaitBlockedexit-path coverage.waitForSync()내의 모든 error path와 early-exit 경로는 반드시 보상 역할의clearStop()에 도달해야 합니다. 누락된 branch가 있으면 stale한 stop data가 다음 STW 사이클까지 남아, debugger의 스레드 상태 정보가 오염됩니다.- STW-epoch race at each polling site.
NeedStopTheWorld확인과waitWithSTWParticipation()/waitForSync()반환 사이의 window에서 새로운 STW 요청이 도달할 수 있습니다. 블로킹 작업 완료 후 다음 polling 전에 VM이 JS로 재진입하면, STW 사이클을 완전히 건너뛰거나 잘못된 epoch counter에 대해notifyVMStop()을 호출하게 됩니다. resumeAll()instepAtBytecode()for the atomics-wait case. atomics-wait에 대한 step-over 시에는 notifier 스레드가 실행될 수 있도록resumeAll()을 사용합니다. step 대상 스레드와 notifier 스레드가 잘못 상호작용하는 경우가 문제입니다. 예를 들어 notifier가 debugger의 breakpoint 설정 전에 waiter를 깨우면, 스레드가 의도한 중단 지점을 지나쳐 실행될 가능성이 있습니다.- Assembly slow-path stop-data threading in
InPlaceInterpreter64.asm. atomics-wait의 경우, stop data(callee, CFR, PC, MC, stack)가 이제 IPInt slow-path 스택 프레임을 통해 전달됩니다. asm이 push하는 내용과 C++ slow path가 stop data로 읽는 내용이 일치하지 않으면, debugger에 잘못된 PC/stack 정보가 노출될 가능성이 있습니다. DebugServer::start()idempotency. 이 버그로 인해 재진입 형태의start()호출이m_serverSocket을 오염시키는 것이 가능했습니다. 현재 guard는isInService()입니다. per-VM 초기화를 경쟁적으로 통과하는 worker VM에서 도달 가능한 TOCTOU window가 없는지 확인이 필요합니다.