From: Davide Libenzi This patch permits a ptrace process on x86 to "see" the instruction following the INT #80h op. This has been tested on 2.6.6 using the appended test source. Running over this: 80485a9: b8 14 00 00 00 mov $0x14,%eax 80485ae: cd 80 int $0x80 80485b0: 89 45 ec mov %eax,0xffffffec(%ebp) 80485b3: eb f4 jmp 80485a9 it produces: waiting ... done: pid=12387 status=1407 sig=5 EIP=0x080485a9 waiting ... done: pid=12387 status=1407 sig=5 EIP=0x080485ae waiting ... done: pid=12387 status=1407 sig=5 EIP=0x080485b0 waiting ... done: pid=12387 status=1407 sig=5 EIP=0x080485b3 (Andi says: "I think this patch is a bad idea. The ptrace handling is traditionally fragile (I remember when merging a rather simple patch from IBM for DR allocation long ago into the suse it broke several debuggers). If you really want to do that wait for 2.7.") ----------------------------- TEST ------------------------------ #include #include #include #include #include #include #include #include #include #include int main(int ac, char **av) { int i, status, res; long start, end; pid_t cpid, pid; struct user_regs_struct ur; struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_DFL; sigaction(SIGCHLD, &sa, NULL); printf("nargs=%d\n", ac); if (ac == 1) goto tracer; printf("arg=%s\n", av[1]); loop: __asm__ volatile ("int $0x80" : "=a" (res) : "0" (__NR_getpid)); goto loop; endloop: exit(0); tracer: if ((cpid = fork()) != 0) goto parent; printf("child=%d\n", getpid()); ptrace(PTRACE_TRACEME, 0, NULL, NULL); execl(av[0], av[0], "child", NULL); exit(0); parent: start = (long) &&loop; end = (long) &&endloop; printf("pchild=%d\n", cpid); for (;;) { pid = wait(&status); if (pid != cpid) continue; res = WSTOPSIG(status); if (ptrace(PTRACE_GETREGS, pid, NULL, &ur)) { printf("[%d] error: ptrace(PTRACE_GETREGS, %d)\n", pid, pid); return 1; } if (ptrace(PTRACE_SINGLESTEP, pid, NULL, res != SIGTRAP ? res: 0)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } if (ur.eip >= start && ur.eip <= end) break; } for (i = 0; i < 15; i++) { printf("waiting ...\n"); pid = wait(&status); printf("done: pid=%d status=%d\n", pid, status); if (pid != cpid) continue; res = WSTOPSIG(status); printf("sig=%d\n", res); if (ptrace(PTRACE_GETREGS, pid, NULL, &ur)) { printf("[%d] error: ptrace(PTRACE_GETREGS, %d)\n", pid, pid); return 1; } printf("EIP=0x%08x\n", ur.eip); if (ptrace(PTRACE_SINGLESTEP, pid, NULL, res != SIGTRAP ? res: 0)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } } if (ptrace(PTRACE_CONT, cpid, NULL, SIGKILL)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } return 0; } Signed-off-by: Andrew Morton --- 25-akpm/arch/i386/kernel/entry.S | 2 +- 25-akpm/arch/i386/kernel/ptrace.c | 7 ++++++- 25-akpm/include/asm-i386/thread_info.h | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff -puN arch/i386/kernel/entry.S~really-ptrace-single-step-2 arch/i386/kernel/entry.S --- 25/arch/i386/kernel/entry.S~really-ptrace-single-step-2 2004-07-12 17:03:11.723015512 -0700 +++ 25-akpm/arch/i386/kernel/entry.S 2004-07-12 17:03:11.730014448 -0700 @@ -375,7 +375,7 @@ syscall_trace_entry: # perform syscall exit tracing ALIGN syscall_exit_work: - testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT), %cl + testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl jz work_pending sti # could let do_syscall_trace() call # schedule() instead diff -puN arch/i386/kernel/ptrace.c~really-ptrace-single-step-2 arch/i386/kernel/ptrace.c --- 25/arch/i386/kernel/ptrace.c~really-ptrace-single-step-2 2004-07-12 17:03:11.725015208 -0700 +++ 25-akpm/arch/i386/kernel/ptrace.c 2004-07-12 17:03:11.731014296 -0700 @@ -147,6 +147,7 @@ void ptrace_disable(struct task_struct * { long tmp; + clear_tsk_thread_flag(child, TIF_SINGLESTEP); tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; put_stack_long(child, EFL_OFFSET, tmp); } @@ -370,6 +371,7 @@ asmlinkage int sys_ptrace(long request, else { clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } + clear_tsk_thread_flag(child, TIF_SINGLESTEP); child->exit_code = data; /* make sure the single step bit is not set. */ tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; @@ -391,6 +393,7 @@ asmlinkage int sys_ptrace(long request, if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; + clear_tsk_thread_flag(child, TIF_SINGLESTEP); /* make sure the single step bit is not set. */ tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; put_stack_long(child, EFL_OFFSET, tmp); @@ -411,6 +414,7 @@ asmlinkage int sys_ptrace(long request, } tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; put_stack_long(child, EFL_OFFSET, tmp); + set_tsk_thread_flag(child, TIF_SINGLESTEP); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); @@ -535,7 +539,8 @@ void do_syscall_trace(struct pt_regs *re audit_syscall_exit(current, regs->eax); } - if (!test_thread_flag(TIF_SYSCALL_TRACE)) + if (!test_thread_flag(TIF_SYSCALL_TRACE) && + !test_thread_flag(TIF_SINGLESTEP)) return; if (!(current->ptrace & PT_PTRACED)) return; diff -puN include/asm-i386/thread_info.h~really-ptrace-single-step-2 include/asm-i386/thread_info.h --- 25/include/asm-i386/thread_info.h~really-ptrace-single-step-2 2004-07-12 17:03:11.726015056 -0700 +++ 25-akpm/include/asm-i386/thread_info.h 2004-07-12 17:03:11.731014296 -0700 @@ -157,7 +157,7 @@ static inline unsigned long current_stac /* work to do on interrupt/exception return */ #define _TIF_WORK_MASK \ - (0x0000FFFF & ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)) + (0x0000FFFF & ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP)) #define _TIF_ALLWORK_MASK 0x0000FFFF /* work to do on any return to u-space */ /* _