Search code examples
pythonptrace

ptrace single step followed by set breakpoint fails


I code a debugger using python's ptrace module. After a debugged program stops on a breakpoint, I do:

  • restore the original instruction on the place of the breakpoint,
  • do a single step,
  • set the breakpoint again,
  • continue the execution of the program.

On my system, if the step 2 (singleStep) immediately follows by the step 3 (createBreakpoint), then something wrong happens with the error message:

ptrace.error.PtraceError: ptrace(cmd=4, ...) error #3: No such process

But if I insert a delay (in the code below using sys.readline), then all the steps and the debugged program are executed successfully.

Quite possible, the error is not specific to ptrace module, maybe I just don't know the correct approach. Any help is welcome.

Python code:

import sys
import ptrace.debugger.child
import ptrace.debugger.debugger

pid = ptrace.debugger.child.createChild(['./sleeper'], 0)
dbg = ptrace.debugger.debugger.PtraceDebugger()
process = dbg.addProcess(pid, is_attached=1)

# use gdb "disassemble main" to find the address of
# the "movel"-instruction between the two "call"s
bp = process.createBreakpoint(0x08048432)

process.cont()
event = process.waitEvent()
print("New process event: %s" % event)
bp.desinstall(set_ip=1)
# Try to reinstall the breakpoint
process.singleStep()
if 1: # otherwise crash?
  print 'Press any key to continue...'
  sys.stdin.readline()
bp = process.createBreakpoint(bp.address)
print("Continue process execution")
process.cont()

C code:

#include <stdio.h>

int main() {
    printf( "~~~~~~~~~~~~> Before breakpoint\n" );
    // The breakpoint
    printf( "~~~~~~~~~~~~> After breakpoint\n" );
    return 0;
}

Solution

  • The man-page for ptrace says:

    PTRACE_SYSCALL, PTRACE_SINGLESTEP

    Restart the stopped tracee as for PTRACE_CONT, but arrange for the tracee to be stopped at the next entry to or exit from a system call, or after execution of a single instruction, respectively. (The tracee will also, as usual, be stopped upon receipt of a signal.) From the tracer's perspective, the tracee will appear to have been stopped by receipt of a SIGTRAP. So, for PTRACE_SYSCALL, for example, the idea is to inspect the arguments to the system call at the first stop, then do another PTRACE_SYSCALL and inspect the return value of the system call at the second stop. The data argument is treated as for PTRACE_CONT. (addr is ignored.)

    I tried to handle the signal by doing nothing, and it helped.

    process.singleStep()
    event = process.waitEvent() # Repair-line
    bp = process.createBreakpoint(bp.address)