http://bugs.winehq.org/show_bug.cgi?id=14697
Summary: wineserver: debug events can block completion of queued user APCs Product: Wine Version: CVS/GIT Platform: PC OS/Version: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: wineserver AssignedTo: wine-bugs@winehq.org ReportedBy: focht@gmx.net
Hello,
consider the following scenario...
A debugger is attached to a debuggee. The debuggee has two threads.
The debugger processes debug events and queries virtual memory info of remote process using VirtualQueryEx in its main loop.
VirtualQueryEx -> NtQueryVirtualMemory -> enqueues APC_VIRTUAL_QUERY user APC for remote process (debuggee). The calling thread is blocked until the APC is completed.
Following is the "good" case:
tid=0009 (debugger process, calling thread, main debugger loop) tid=001a (debuggee thread 1)
--- snip --- .. 0009: queue_apc( thread=(nil), process=0x54, call={APC_VIRTUAL_QUERY,addr=0x7fff0000} ) 001a: *wakeup* signaled=192 cookie=0x7ffdb7b4 0009: queue_apc() = 0 { handle=0x68, self=0 } 0009: select( flags=4, cookie=0x32d30c, signal=(nil), prev_apc=(nil), timeout=infinite, result={}, handles={0x68} ) 0009: select() = PENDING { apc_handle=(nil), timeout=infinite, call={APC_NONE} } 001a: select( flags=4, cookie=0x7ffdb7b4, signal=(nil), prev_apc=(nil), timeout=1c8f097946d2824 (-0.1787600), result={}, handles={} ) 001a: select() = USER_APC { apc_handle=0x68, timeout=1c8f097946d2824 (-0.1787600), call={APC_VIRTUAL_QUERY,addr=0x7fff0000} } 001a: select( flags=4, cookie=0x7ffdb7b4, signal=(nil), prev_apc=0x68, timeout=1c8f097946d2824 (-0.1792110), result={APC_VIRTUAL_QUERY,status=40000002,base=(nil),alloc_base=(nil),size=0,state=0,prot=0,alloc_prot=0,alloc_type=0}, handles={} ) 0009: *wakeup* signaled=0 cookie=0x32d30c 001a: select() = PENDING { apc_handle=(nil), timeout=1c8f097946d2824 (-0.1792110), call={APC_NONE} } 0009: get_apc_result( handle=0x68 ) 0009: get_apc_result() = 0 { result={APC_VIRTUAL_QUERY,status=40000002,base=(nil),alloc_base=(nil),size=0,state=0,prot=0,alloc_prot=0,alloc_type=0} } --- snip ---
Now the interesting one, which ultimately leads to block of calling thread ...
tid=0009 (debugger process, calling thread, main debugger loop) tid=001a (debuggee thread 1) tid=001b (debuggee thread 2)
--- snip --- 0009: continue_debug_event( pid=0019, tid=001a, status=65538 ) 001a: *wakeup* signaled=258 cookie=0x7ffdb7b4 001b: *wakeup* signaled=258 cookie=0x7ffd77b4 0009: continue_debug_event() = 0 .. 0009: read_process_memory( handle=0x54, addr=0x7ffd8000 ) 001a: *signal* signal=19 0009: read_process_memory() = 0 { data= {} } 0009: read_process_memory( handle=0x54, addr=0x7ffd4000 ) 001a: *signal* signal=19 0009: read_process_memory() = 0 { data={} } 0009: queue_apc( thread=(nil), process=0x54, call={APC_VIRTUAL_QUERY,addr=(nil)} ) 001b: *wakeup* signaled=192 cookie=0x7ed44874 0009: queue_apc() = 0 { handle=0x68, self=0 } 001a: load_dll( handle=(nil), base=0x61090000, size=102400, dbg_offset=0, dbg_size=0, name=0x13e468, filename=L"C:\windows\system32\msacm32.dll" ) 001a: *sent signal* signal=10 001b: *sent signal* signal=10 001a: load_dll() = 0 .. 0009: select( flags=4, cookie=0x32d30c, signal=(nil), prev_apc=(nil), timeout=infinite, result={}, handles={0x68} ) 0009: select() = PENDING { apc_handle=(nil), timeout=infinite, call={APC_NONE} } 001a: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context= {...}) 001a: set_thread_context() = 0 { self=1 } 001b: select( flags=4, cookie=0x7ed44874, signal=(nil), prev_apc=(nil), timeout=infinite, result={}, handles={0x4c} ) 001b: select() = USER_APC { apc_handle=0x68, timeout=infinite, call={APC_VIRTUAL_QUERY,addr=(nil)} } 001a: select( flags=4, cookie=0x7ffdb7b4, signal=(nil), prev_apc=(nil), timeout=0, result={}, handles={} ) 001a: select() = PENDING { apc_handle=(nil), timeout=1c8f09794c8b694 (+0.0000000), call={APC_NONE} } 001b: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1, context= {...}) 001b: set_thread_context() = 0 { self=1 } 001b: select( flags=4, cookie=0x7ffd77b4, signal=(nil), prev_apc=(nil), timeout=0, result={}, handles={} ) 001b: select() = PENDING { apc_handle=(nil), timeout=1c8f09794c90dc4 (+0.0000000), call={APC_NONE} } ... --- snip ---
At this point, tid=0009 (debugger) is blocked forever.
When the APC_VIRTUAL_QUERY user APC is queued, remote thread 001b is selected/destined to carry out the APC.
After thread 001a and 001b woke up, a dynamic library is loaded from thread 001a which results in LOAD_DLL_DEBUG_EVENT dispatched. This initiates the suspension of all threads in the child process (SIGUSR1 is sent for 001a and 001b).
Thread 001a is suspended first, 001b next. The suspension of 001b happens to a time when 001b is about to carry out the APC_VIRTUAL_QUERY.
Meanwhile thread 0009 (calling process) is waiting for APC completion result forever because it's blocked by VirtualQueryEx in debugger loop, unable to receive and process any debug event.
Regards