
From: Roland McGrath <roland@redhat.com>

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 <signal.h>
	#include <stdlib.h>
	#include <string.h>

	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 <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 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:
_
