[Bug 25462] Unable to break using 'Break All' button while remote debugging with VS2008 (software breakpoint overwrites syscall instruction in VDSO page)

wine-bugs at winehq.org wine-bugs at winehq.org
Sat Nov 22 18:57:42 CST 2014


https://bugs.winehq.org/show_bug.cgi?id=25462

Anastasius Focht <focht at 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 at 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 at 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

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list