https://bugs.winehq.org/show_bug.cgi?id=25462
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |download Status|UNCONFIRMED |NEW URL| |http://download.microsoft.c | |om/download/8/1/d/81d3f35e- | |fa03-485b-953b-ff952e402520 | |/VS2008ProEdition90dayTrial | |ENUX1435622.iso CC| |focht@gmx.net Summary|Unable to break while |Unable to break using |remote debugging with |'Break All' button while |VS2008 |remote debugging with | |VS2008 (software breakpoint | |overwrites syscall | |instruction in VDSO page) Ever confirmed|0 |1
--- Comment #3 from Anastasius Focht focht@gmx.net --- Hello folks,
confirming.
--- snip --- $ pwd /home/focht/.wine/drive_c/Program Files/Microsoft Visual Studio 9.0/Common7/IDE
$ WINEDEBUG=+tid,+seh,+relay,+server wine ./devenv.exe >>log.txt 2>&1 ... 0063:Call KERNEL32.DebugActiveProcess(00000043) ret=459cff6a 0063: debug_process( pid=0043, attach=1 ) 0044: *sent signal* signal=10 0044: *signal* signal=10 0044: *signal* signal=19 0063: debug_process() = 0 0063:Ret KERNEL32.DebugActiveProcess() retval=00000001 ret=459cff6a ... 0063:Call KERNEL32.OpenProcess(001f0fff,00000000,00000043) ret=4597f43a 0044: set_suspend_context( context={cpu=x86,eip=f772c42e,esp=0033f6c8,ebp=0033f718,eflags=00200296,cs=0023,ss=002b,ds=002b,es=002b,fs=0063,gs=006b,eax=00000003,ebx=00000007,ecx=0033f6f8,edx=00000010,esi=0033fae0,edi=00000000,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,fp.ctrl=ffff027f,fp.status=ffff0020,fp.tag=ffffffff,fp.err_off=7e75bec3,fp.err_sel=00000023,fp.data_off=0033cf78,fp.data_sel=ffff002b,fp.cr0npx=00000020,fp.reg0=0,fp.reg1=0,fp.reg2=0,fp.reg3=0,fp.reg4=0,fp.reg5=1,fp.reg6=0,fp.reg7=1,extended={...}} ) 0044: set_suspend_context() = 0 0063: open_process( pid=0043, access=001f0fff, attributes=00000000 ) 0063: open_process() = 0 { handle=0970 } 0044: select( flags=2, cookie=7ffdb33c, timeout=0, prev_apc=0000, result={}, data={} ) 0044: select() = PENDING { timeout=1d0068cf0915320 (+0.0000000), call={APC_NONE}, apc_handle=0000 } 0063:Ret KERNEL32.OpenProcess() retval=00000970 ret=4597f43a ... 000d:Call KERNEL32.SuspendThread(0000096c) ret=459cba5f 000d: suspend_thread( handle=096c ) 0044: *sent signal* signal=10 000d: suspend_thread() = 0 { count=0 } 000d:Ret KERNEL32.SuspendThread() retval=00000000 ret=459cba5f ... 000d:Call KERNEL32.GetThreadContext(0000096c,0795ce08) ret=45969217 0044: set_suspend_context( context={cpu=x86,eip=f772c42e,esp=0033f6c8,ebp=0033f718,eflags=00200296,cs=0023,ss=002b,ds=002b,es=002b,fs=0063,gs=006b,eax=00000003,ebx=00000007,ecx=0033f6f8,edx=00000010,esi=0033fae0,edi=00000000,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,fp.ctrl=ffff027f,fp.status=ffff0020,fp.tag=ffffffff,fp.err_off=7e75bec3,fp.err_sel=00000023,fp.data_off=0033cf78,fp.data_sel=ffff002b,fp.cr0npx=00000020,fp.reg0=0,fp.reg1=0,fp.reg2=0,fp.reg3=0,fp.reg4=0,fp.reg5=1,fp.reg6=0,fp.reg7=1,extended={...}} ) 0044: set_suspend_context() = 0 000d: get_thread_context( handle=096c, flags=00000021, suspend=1 ) 0044: *signal* signal=19 000d: get_thread_context() = 0 { self=0, context={cpu=x86,eip=f772c42e,esp=0033f6c8,ebp=0033f718,eflags=00200296,cs=0023,ss=002b,dr0=00000000,dr1=00000000,dr2=00000000,dr3=00000000,dr6=00000000,dr7=00000000,extended={...}} } 000d:Ret KERNEL32.GetThreadContext() retval=00000001 ret=45969217 ... 0044: select( flags=2, cookie=7ffdb33c, timeout=0, prev_apc=0000, result={}, data={} ) 0044: select() = PENDING { timeout=1d0068cf68d95a4 (+0.0000000), call={APC_NONE}, apc_handle=0000 } ... 000d:Call KERNEL32.ReadProcessMemory(00000970,f772c42e,0795cd9f,00000001,0795cd54) ret=45968ebd 000d: read_process_memory( handle=0970, addr=f772c42e ) 0044: *signal* signal=19 000d: read_process_memory() = 0 { data={cd} } 000d:Ret KERNEL32.ReadProcessMemory() retval=00000001 ret=45968ebd ... 000d:Call KERNEL32.ReadProcessMemory(00000970,f772c42e,003f5cb0,00000001,0795cc60) ret=45968ebd 000d: read_process_memory( handle=0970, addr=f772c42e ) 0044: *signal* signal=19 000d: read_process_memory() = 0 { data={cd} } 000d:Ret KERNEL32.ReadProcessMemory() retval=00000001 ret=45968ebd 000d:Call KERNEL32.WriteProcessMemory(00000970,f772c42e,0795cd17,00000001,0795cc64) ret=459697bd 000d: write_process_memory( handle=0970, addr=f772c42e, data={cc} ) 0044: *signal* signal=19 000d: write_process_memory() = 0 000d:Ret KERNEL32.WriteProcessMemory() retval=00000001 ret=459697bd 000d:Call KERNEL32.FlushInstructionCache(00000970,f772c42e,00000001) ret=45969812 000d:Ret KERNEL32.FlushInstructionCache() retval=00000001 ret=45969812 000d:Call KERNEL32.GetThreadContext(0000096c,003fad14) ret=45969217 000d: get_thread_context( handle=096c, flags=0000002f, suspend=1 ) 0044: *exited* status=1 000d: get_thread_context() = ACCESS_DENIED { self=0, context={cpu=x86,eip=f772c42e,esp=0033f6c8,ebp=0033f718,eflags=00200296,cs=0023,ss=002b,ds=002b,es=002b,fs=0063,gs=006b,eax=00000003,ebx=00000007,ecx=0033f6f8,edx=00000010,esi=0033fae0,edi=00000000,fp.ctrl=ffff027f,fp.status=ffff0020,fp.tag=ffffffff,fp.err_off=7e75bec3,fp.err_sel=00000023,fp.data_off=0033cf78,fp.data_sel=ffff002b,fp.cr0npx=00000020,fp.reg0=0,fp.reg1=0,fp.reg2=0,fp.reg3=0,fp.reg4=0,fp.reg5=1,fp.reg6=0,fp.reg7=1,extended={...}} } 0044: *killed* exit_code=0 0063: *wakeup* signaled=0 0057: *wakeup* signaled=0 0043: *process killed* 000d:Ret KERNEL32.GetThreadContext() retval=00000000 ret=45969217 ... 000d:Call KERNEL32.ResumeThread(0000096c) ret=459cbaf7 000d: resume_thread( handle=096c ) 000d: resume_thread() = 0 { count=1 } 000d:Ret KERNEL32.ResumeThread() retval=00000001 ret=459cbaf7 ... 0063: wait_debug_event( get_handle=1 ) 0063: wait_debug_event() = 0 { pid=0043, tid=0044, wait=0000, event={exit_process,code=0} } 0063:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=459419d4 ... 0063:Call KERNEL32.WriteProcessMemory(00000970,f772c42e,003f5cb0,00000001,09f7e880) ret=459697bd 0063: write_process_memory( handle=0970, addr=f772c42e, data={cd} ) 0063: write_process_memory() = PROCESS_IS_TERMINATING 0063:Ret KERNEL32.WriteProcessMemory() retval=00000000 ret=459697bd --- snip ---
Terminal output with 'notepad' as debuggee during breakin:
--- snip --- err:seh:setup_exception_record nested exception on signal stack in thread 0044 eip f772c42f esp 7ffdb208 stack 0x242000-0x340000 --- snip ---
The IDE debugger thread writes a software breakpoint to the child main thread EIP which happens to be the Linux syscall instruction living in special PER_CPU segment/VDSO shared page.
--- snip --- -> __kernel_vsyscall __libc_read wait_select_reply server_select NtWaitForMultipleObjects ... --- snip ---
'winedbg' to disassemble/illustrate:
--- snip --- Wine-dbg>attach 0x8 0xf77af42e __kernel_vsyscall+0xe in [vdso].so: int $0x80
Wine-dbg>x/17i 0xf77af420 0xf77af420 __kernel_vsyscall in [vdso].so: pushl %ecx 0xf77af421 __kernel_vsyscall+0x1 in [vdso].so: pushl %edx 0xf77af422 __kernel_vsyscall+0x2 in [vdso].so: pushl %ebp 0xf77af423 __kernel_vsyscall+0x3 in [vdso].so: movl %esp,%ebp 0xf77af425 __kernel_vsyscall+0x5 in [vdso].so: sysenter 0xf77af427 __kernel_vsyscall+0x7 in [vdso].so: nop 0xf77af428 __kernel_vsyscall+0x8 in [vdso].so: nop 0xf77af429 __kernel_vsyscall+0x9 in [vdso].so: nop 0xf77af42a __kernel_vsyscall+0xa in [vdso].so: nop 0xf77af42b __kernel_vsyscall+0xb in [vdso].so: nop 0xf77af42c __kernel_vsyscall+0xc in [vdso].so: nop 0xf77af42d __kernel_vsyscall+0xd in [vdso].so: nop 0xf77af42e __kernel_vsyscall+0xe in [vdso].so: int $0x80 0xf77af430 __kernel_vsyscall+0x10 in [vdso].so: popl %ebp 0xf77af431 __kernel_vsyscall+0x11 in [vdso].so: popl %edx 0xf77af432 __kernel_vsyscall+0x12 in [vdso].so: popl %ecx 0xf77af433 __kernel_vsyscall+0x13 in [vdso].so: ret --- snip ---
'ptrace' is used to have this work (see tidbit about special handling for VDSO).
The (re)execution of the syscall instruction on the client side through signal handler code (ptrace suspend/context retrieval -> SIGUSR1) triggers a SIGTRAP because the syscall instruction itself was overwritten by the softbp. Unfortunately the debuggee won't be able to send a debugging event unless it removes the softbp on its own or uses private/inlined syscall call sites.
I'm inclined to say WONTFIX.
---
Tidbit:
The PER_CPU segment/VDSO shared page is usually write-protected for application access. The 'ptrace' interface allows to bypass this limitation, otherwise you would not be able to set software breakpoints.
--- quote --- VM_MAYWRITE is placed into the VMA representing the VDSO by install_special_mapping. A read only, executable pte will then be placed down later on fault.
The debugger will at some point call ptrace with PTRACE_POKETEXT to set a breakpoint in the VDSO. This will lead to a call to generic_ptrace_pokedata which will call access_process_vm which will call get_user_pages with the force and write flags set. As force is set, get_user_pages uses VM_MAYWRITE to determine whether or not writes are supported. A call to handle_mm_fault with FAULT_FLAG_WRITE is then issued and that results in a COW taking place (as the userspace pte is not writeable). The new COWed page will still not be writeable (as the VMA does not have the VM_WRITE flag), but it will be a separate copy. access_process_vm will then kmap the COWed page and write data to it. So at the end we have modified .text that will still not be writeable by userspace.
Subsequent ptraces with PTRACE_POKETEXT will lead to do_wp_page re-using the COW'ed page as it will be anonymous with only one reference (unless a fork took place). --- quote ---
$ sha1sum VS2008ProEdition90dayTrialENUX1435622.iso bf671c81c0d097f4261b232c80b38bd9549294b0 VS2008ProEdition90dayTrialENUX1435622.iso
$ du -sh VS2008ProEdition90dayTrialENUX1435622.iso 3.4G VS2008ProEdition90dayTrialENUX1435622.iso
$ wine --version wine-1.7.31-99-g5ecea72
Regards