[Bug 10478] New: wine server get/set thread context doesn' t work correctly if simultaneously used by debugger and debuggee

wine-bugs at winehq.org wine-bugs at winehq.org
Sat Nov 17 05:34:24 CST 2007


http://bugs.winehq.org/show_bug.cgi?id=10478

           Summary: wine server get/set thread context doesn't work
                    correctly if simultaneously used by debugger and
                    debuggee
           Product: Wine
           Version: CVS/GIT
          Platform: PC
        OS/Version: Linux
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: wine-debug
        AssignedTo: wine-bugs at winehq.org
        ReportedBy: focht at gmx.net


Created an attachment (id=9209)
 --> (http://bugs.winehq.org/attachment.cgi?id=9209)
WINEDEBUG=+tid,+server,+win trace of debugging session

Hello,

while debugging a target with some debugger I experienced severe problems with
wine server get/set thread context implementation.
It doesn't work correctly if the get/set thread context API is simultaneously
used by both, the debugger and the debuggee to manage exception events/control
execution transfer.

Attached is WINEDEBUG=+tid,+seh,+win trace (don't bother with +win channel
output, was for my own purpose).

pid=0008, tid=0009 debugger main thread
pid=0008, tid=000d debugger debug event loop thread

pid=000e, tid=000f debuggee main thread
pid=000e, tid=0010 debuggee child thread
pid=000e, tid=0011 debuggee child thread
pid=000e, tid=0012 debuggee child thread

Search for "000d: get_thread_context() = ACCESS_DENIED { self=0, context={} }"
in attached log and walk your way back...

--- snip ---
000f: queue_exception_event( first=1,
record={context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=00004000,esi=00004000,edi=00197430,ebp=0034e5d4,eip=7b841378,esp=0034e570,eflags=00200216,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=a3f67070,dr1=0012e150,dr2=0034eec0,dr3=00000000,dr6=00000006,dr7=00000200,float={00000003,0034e234,33746e49,00000032,00000000,000000f8,0034e280,0034e268,00087510,00198af0,00110014,00000001,00000318,00000000,4d430002,4d430000,00198e18,00000010,00000000,00000000,00000000,4d430003,00000000,00000000,00000000,00000000,00000000,00000000}},rec={code=e06d7363,flags=1,rec=(nil),addr=0x7b841300,params={19930520,34e670,79f9acc4}}
)
000d: *wakeup* signaled=0 cookie=0x616e24c0
0010: *sent signal* signal=10
0011: *sent signal* signal=10
0012: *sent signal* signal=10
000f: queue_exception_event() = 0 { handle=0x244 } 
--- snip ---

Debuggee main thread throws first chance C++ exception (queues event). Ok.

--- snip ---
0010: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1,
context={flags=0001001f,eax=00000003,ebx=0000001f,ecx=613516a4,edx=00000008,esi=613516a4,edi=7ffd4000,ebp=613516b8,eip=600007f0,esp=61351670,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff4000,ffffffff,604b434d,035f0073,0034dc34,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffe,00000000,40038000,00004000}}
)
0010: set_thread_context() = 0 { self=1 }
0011: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1,
context={flags=0001001f,eax=00000003,ebx=00000023,ecx=614624a0,edx=00000008,esi=614624a0,edi=7ffd0000,ebp=614624b4,eip=600007f0,esp=6146246c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0100,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffd,80000000,40078662,00000100}}
)
0011: set_thread_context() = 0 { self=1 }
0012: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1,
context={flags=0001001f,eax=00000003,ebx=0000003a,ecx=6c101d70,edx=00000008,esi=6c101d70,edi=7ffcc000,ebp=6c101d84,eip=600007f0,esp=6c101d3c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0120,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,00000000,00000000,80000000,00003ffd,60000000,400785bc,00000120}}
)
0012: set_thread_context() = 0 { self=1 } 
--- snip ---

The debuggee child threads get SIGUSR1, suspend themselves (on purpose!). Ok.

--- snip ---
000d: wait_debug_event( get_handle=1 )
000d: wait_debug_event() = 0 { pid=000e, tid=000f, wait=(nil),
event={exception,{code=e06d7363,flags=1,rec=(nil),addr=0x7b841300,params={19930520,34e670,79f9acc4},first=1}
}
..
000d: continue_debug_event( pid=000e, tid=000f, status=-2147418111 )
000f: *wakeup* signaled=0 cookie=0x34dfd4
0010: *wakeup* signaled=258 cookie=0x7ffd77b4
0011: *wakeup* signaled=258 cookie=0x7ffd37b4
000d: continue_debug_event() = 0
..
--- snip ---

Debugger wakes up and sees first chance C++ exception, decides to pass it to
debuggee (resumes execution of threads). Ok.

--- snip ---
000f: get_exception_status( handle=0x244 )
000f: get_exception_status() = 80010001 {
context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=00004000,esi=00004000,edi=00197430,ebp=0034e5d4,eip=7b841378,esp=0034e570,eflags=00200216,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=a3f67070,dr1=0012e150,dr2=0034eec0,dr3=00000000,dr6=00000006,dr7=00000200,float={00000003,0034e234,33746e49,00000032,00000000,000000f8,0034e280,0034e268,00087510,00198af0,00110014,00000001,00000318,00000000,4d430002,4d430000,00198e18,00000010,00000000,00000000,00000000,4d430003,00000000,00000000,00000000,00000000,00000000,00000000}}
}
0010: get_thread_context( handle=0xfffffffe, flags=00010007, suspend=1 )
0010: get_thread_context() = 0 { self=1,
context={flags=0001001f,eax=00000003,ebx=0000001f,ecx=613516a4,edx=00000008,esi=613516a4,edi=7ffd4000,ebp=613516b8,eip=600007f0,esp=61351670,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff4000,ffffffff,604b434d,035f0073,0034dc34,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffe,00000000,40038000,00004000}}
}
0011: get_thread_context( handle=0xfffffffe, flags=00010007, suspend=1 )
0011: get_thread_context() = 0 { self=1,
context={flags=0001001f,eax=00000003,ebx=00000023,ecx=614624a0,edx=00000008,esi=614624a0,edi=7ffd0000,ebp=614624b4,eip=600007f0,esp=6146246c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0100,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffd,80000000,40078662,00000100}}
}
000d: select( flags=4, cookie=0x616e24c0, signal=(nil), prev_apc=(nil),
timeout=+1.0000000, result={}, handles={0xa8} )
000d: select() = PENDING { apc_handle=(nil), timeout=1c828ffa61af642
(+1.0000000), call={APC_NONE} }
0012: get_thread_context( handle=0xfffffffe, flags=00010007, suspend=1 )
0012: get_thread_context() = 0 { self=1,
context={flags=0001001f,eax=00000003,ebx=0000003a,ecx=6c101d70,edx=00000008,esi=6c101d70,edi=7ffcc000,ebp=6c101d84,eip=600007f0,esp=6c101d3c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0120,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,00000000,00000000,80000000,00003ffd,60000000,400785bc,00000120}}
}
000f: get_process_info( handle=0xffffffff )
000f: get_process_info() = 0 { pid=000e, ppid=0008, exit_code=259, priority=2,
affinity=1, peb=0x7ffdf000, start_time=1c828ff9f2037c6 (-10.7374150),
end_time=0 }
000f: queue_exception_event( first=1,
record={context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=0034ee58,esi=0034ee58,edi=e0434f4d,ebp=0034ee20,eip=7b841378,esp=0034edbc,eflags=00200212,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=02d410d8,dr2=a3f67060,dr3=00000000,dr6=00110000,dr7=00000002,float={0034eaa4,00001302,00198d10,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001,0034eadc,79e783ca,00110000,00000000,79e783e6,a3f67cf4,00000001,00197430,00000000,00000008,00000000,0034eb14,7a3296dc,ffffffff,79e783e6,79e7839d,00110000}},rec={code=e0434f4d,flags=1,rec=(nil),addr=0x7b841300,params={80004002}}
)
--- snip ---

Main debuggee thread SEH evaluates first chance exception and converts it
internally to CLR exception, throwing again.

--- snip ---
0010: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1,
context={flags=0001001f,eax=00000003,ebx=0000001f,ecx=613516a4,edx=00000008,esi=613516a4,edi=7ffd4000,ebp=613516b8,eip=600007f0,esp=61351670,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff4000,ffffffff,604b434d,035f0073,0034dc34,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffe,00000000,40038000,00004000}}
)
0010: set_thread_context() = 0 { self=1 }
0011: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1,
context={flags=0001001f,eax=00000003,ebx=00000023,ecx=614624a0,edx=00000008,esi=614624a0,edi=7ffd0000,ebp=614624b4,eip=600007f0,esp=6146246c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0100,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,3ffe8000,00000000,80000000,00003ffd,80000000,40078662,00000100}}
)
0011: set_thread_context() = 0 { self=1 }
0012: set_thread_context( handle=0xfffffffe, flags=00010007, suspend=1,
context={flags=0001001f,eax=00000003,ebx=0000003a,ecx=6c101d70,edx=00000008,esi=6c101d70,edi=7ffcc000,ebp=6c101d84,eip=600007f0,esp=6c101d3c,eflags=00200293,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={ffff037f,ffff0120,ffffffff,79e7a66b,06d90073,00000000,0000007b,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,3fff8000,00000000,80000000,00003ffe,00000000,00000000,00000000,80000000,00003ffd,60000000,400785bc,00000120}}
)
0012: set_thread_context() = 0 { self=1 } 
--- snip ---

The debuggee child threads get SIGUSR1, suspend themselves again (on purpose!).
Ok.

--- snip ---
000d: suspend_thread( handle=0xd4 )
000d: suspend_thread() = 0 { count=0 }
000d: suspend_thread( handle=0xd8 )
000d: suspend_thread() = 0 { count=0 }
000d: suspend_thread( handle=0xf8 )
000d: suspend_thread() = 0 { count=0 }
000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 )
000f: *signal* signal=19
000d: get_thread_context() = 0 { self=0,
context={flags=0001003f,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=0034ee58,esi=0034ee58,edi=e0434f4d,ebp=0034ee20,eip=7b841378,esp=0034edbc,eflags=00200212,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,float={0034eaa4,00001302,00198d10,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001,0034eadc,79e783ca,00110000,00000000,79e783e6,a3f67cf4,00000001,00197430,00000000,00000008,00000000,0034eb14,7a3296dc,ffffffff,79e783e6,79e7839d,00110000}}
}
000d: read_process_memory( handle=0xb8, addr=0x7b841378 )
000f: *signal* signal=19
000d: read_process_memory() = 0 { data={8b} }
000d: resume_thread( handle=0xbc )
000d: resume_thread() = 0 { count=1 }
000d: resume_thread( handle=0xd4 )
000d: resume_thread() = 0 { count=1 }
000d: resume_thread( handle=0xd8 )
000d: resume_thread() = 0 { count=1 }
000d: resume_thread( handle=0xf8 )
000d: resume_thread() = 0 { count=1 }
000d: continue_debug_event( pid=000e, tid=000f, status=-2147418111 )
000f: *wakeup* signaled=0 cookie=0x34e824
0010: *wakeup* signaled=258 cookie=0x7ffd77b4
0011: *wakeup* signaled=258 cookie=0x7ffd37b4
0012: *wakeup* signaled=258 cookie=0x7ffcf7b4
000d: continue_debug_event() = 0 
--- snip ---

Debugger wakes up again, now seeing the CLR exception.
Debugger suspends debuggee threads - although unnecessary due to debugging
event before = already suspended.
This exception type is unhandled, main debuggee thread receives SIGSTOP.
Execution of debuggee threads resumed.

--- snip ---
000f: get_exception_status( handle=0x244 )
000f: get_exception_status() = 80010001 {
context={flags=00010007,eax=7b82c455,ebx=7b8ad1e4,ecx=00000000,edx=0034ee58,esi=0034ee58,edi=e0434f4d,ebp=0034ee20,eip=7b841378,esp=0034edbc,eflags=00200212,cs=0073,ds=007b,es=007b,fs=0033,gs=003b,dr0=00000000,dr1=
..
000f: *killed* exit_code=0 
--- snip ---

Debuggee main thread dies. Ok.

--- snip ---
000d: wait_debug_event( get_handle=1 )
000d: wait_debug_event() = 0 { pid=000e, tid=000f, wait=(nil),
event={exit_thread,code=0} }
000d: suspend_thread( handle=0xd4 )
000d: suspend_thread() = 0 { count=0 }
000d: suspend_thread( handle=0xd8 )
000d: suspend_thread() = 0 { count=0 }
000d: suspend_thread( handle=0xf8 )
000d: suspend_thread() = 0 { count=0 }
000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 )
000d: get_thread_context() = ACCESS_DENIED { self=0, context={} }
000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 )
000d: get_thread_context() = ACCESS_DENIED { self=0, context={} }
000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 )
000d: get_thread_context() = ACCESS_DENIED { self=0, context={} }
000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 )
000d: get_thread_context() = ACCESS_DENIED { self=0, context={} }
000d: get_thread_context( handle=0xbc, flags=0001003f, suspend=0 )
000d: get_thread_context() = ACCESS_DENIED { self=0, context={} } 
000d: read_process_memory( handle=0xb8, addr=(nil) )
000d: read_process_memory() = ACCESS_DENIED { data={} } 
..
--- snip ---

Debugger wakes up, sees debuggee (main) thread termination event.
Suspends remaining debuggee threads - although unnecessary due to debugging
event before = already suspended.

Now the problem: the debugger can't get the thread context of any debuggee
threads nor process memory anymore after this point.
Basically dead end here...

The reason is the way server/thread.c/get_thread_context() and
server/thread.c/set_thread_context() handles thread->context and
thread->suspend_context data ("self" vs. "foreign" threads)

child threads "self":

set_thread_context() -> suspend == 1 -> thread->suspend_context filled
get_thread_context(): req->suspend == 1 && thread == current &&
thread->suspend_context

--- snip ---
 if (thread->context == thread->suspend_context) thread->context = NULL;
            set_reply_data_ptr( thread->suspend_context, sizeof(CONTEXT) );
            thread->suspend_context = NULL; 
--- snip ---

Which resets the context and suspend context data.

Next time when the debugger itself tries to query the thread context of already
suspended debuggee child threads it will obviously fail:

"foreign" debugger thread:

get_thread_context() -> suspend == 0

--- snip ---
 else if (thread != current && !thread->context)
    {
        /* thread is not suspended, retry (if it's still running) */
        if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED );
        else set_error( STATUS_PENDING );
    } 
--- snip ---

I guess this is an oversight, a behavior not really intended?

Regards


-- 
Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are watching all bug changes.



More information about the wine-bugs mailing list