From: Roland McGrath With Davide Libenzi's patch for i386 and my follow-on patch for x86-64's ia32 support, single-stepping through any system call correctly traps immediately on return to user mode. I still don't know why the same problem doesn't arise for `syscall'/`sysret' on native 64-bit x86-64, and would like someone to point me at what I misunderstood in the chip manuals. But, there is a problem in the case of the rt_sigreturn system call on native x86-64. When using PTRACE_SINGLESTEP to step into an rt_sigreturn system call and the sigcontext being restored does not have TF set in its %eflags, then we miss the single-step trap. This makes gdb unhappy. Here is a test program to run under gdb. Set a breakpoint on "keeper". When SIGSEGV hits, just continue to let the program handle it. When you get into "keeper", do "display/i $pc" to see what's going on and then do "stepi" repeatedly to get out of the handler and into the signal handler trampoline as it makes the rt_sigreturn system call. When you step into the `syscall' instruction, you will lose control. Rather than stopping immediately, the program will reexecute the faulting instruction and take a second SIGSEGV. #include #include #include extern void keeper (int sig) { } volatile long v1 = 0; volatile long v2 = 0; volatile long v3 = 0; extern long bowler (void) { /* Try to read address zero. Do it in a slightly convoluted way so that more than one instruction is used. */ return *(char *) (v1 + v2 + v3); } int main () { static volatile int i; struct sigaction act; memset (&act, 0, sizeof act); act.sa_handler = keeper; sigaction (SIGSEGV, &act, NULL); bowler (); return 0; } This patch fixes the problem by forcing a fake single-step trap at the end of rt_sigreturn when PTRACE_SINGLESTEP was used to enter the system call. Signed-off-by: Roland McGrath Signed-off-by: Andrew Morton --- 25-akpm/arch/x86_64/kernel/signal.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+) diff -puN arch/x86_64/kernel/signal.c~x86-64-singlestep-through-sigreturn-system-call arch/x86_64/kernel/signal.c --- 25/arch/x86_64/kernel/signal.c~x86-64-singlestep-through-sigreturn-system-call 2004-07-12 21:42:56.155393944 -0700 +++ 25-akpm/arch/x86_64/kernel/signal.c 2004-07-12 21:42:56.159393336 -0700 @@ -92,6 +92,7 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long *prax) { unsigned int err = 0; + unsigned int caller_eflags; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; @@ -112,6 +113,7 @@ restore_sigcontext(struct pt_regs *regs, { unsigned int tmpflags; err |= __get_user(tmpflags, &sc->eflags); + caller_eflags = regs->eflags; regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); regs->orig_rax = -1; /* disable syscall checks */ } @@ -128,6 +130,22 @@ restore_sigcontext(struct pt_regs *regs, } err |= __get_user(*prax, &sc->rax); + + if (!err && unlikely(caller_eflags & X86_EFLAGS_TF) && + (current->ptrace & (PT_PTRACED|PT_DTRACE)) == (PT_PTRACED|PT_DTRACE)) { + /* + * If ptrace single-stepped into the sigreturn system call, + * then fake a single-step trap before we resume the restored + * context. + */ + siginfo_t info; + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + info.si_addr = (void *)regs->rip; + force_sig_info(SIGTRAP, &info, current); + } + return err; badframe: _