Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 5 Jan 2002 00:00:08 -0800 (PST)
From:      Bruce Evans <bde@zeta.org.au>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: ptrace bug was Re: gnu/33262: gdb does not handle pending signals correctly when single stepping
Message-ID:  <200201050800.g05808m62326@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR gnu/33262; it has been noted by GNATS.

From: Bruce Evans <bde@zeta.org.au>
To: k Macy <kip_macy@yahoo.com>
Cc: <freebsd-gnats-submit@FreeBSD.ORG>
Subject: Re: ptrace bug was Re: gnu/33262: gdb does not handle pending signals
 correctly when single stepping
Date: Sat, 5 Jan 2002 18:59:00 +1100 (EST)

 On Thu, 3 Jan 2002, k Macy wrote:
 
 >  Not to mention that SIGVTALRM is already used by
 >  the thread library (although I would hope that
 >  _thread_sys_sigaction is smart enough to handle
 >  that case). I've stepped through the GDB
 >  code on both 4.18 and 5.1. On 5.1 I found the
 >  following in i386fbsd-nat.c:
 >
 >  void
 >  child_resume (ptid_t ptid, int step, enum
 >  target_signal signal)
 >  {
 >    pid_t pid = ptid_get_pid (ptid);
 >    int request = PT_STEP;
 >
 >    if (pid == -1)
 >      /* Resume all threads.  This only gets used in the
 >  non-threaded
 >         case, where "resume all threads" and "resume
 >  inferior_ptid" are
 >         the same.  */
 >      pid = ptid_get_pid (inferior_ptid);
 >
 >    if (!step)
 >      {
 >        unsigned int eflags;
 >
 >        /* Workaround for a bug in FreeBSD.  Make sure
 >  that the trace
 >           flag is off when doing a continue.  There is
 >  a code path
 >           through the kernel which leaves the flag set
 >  when it should
 >           have been cleared.  If a process has a signal
 >  pending (such
 >           as SIGALRM) and we do a PT_STEP, the process
 >  never really has
 >           a chance to run because the kernel needs to
 >  notify the
 >           debugger that a signal is being sent.
 >  Therefore, the process
 >           never goes through the kernel's trap()
 >  function which would
 >           normally clear it.  */
 >
 >        eflags = read_register (PS_REGNUM);
 >        if (eflags & 0x0100)
 >          write_register (PS_REGNUM, eflags & ~0x0100);
 >
 >        request = PT_CONTINUE;
 >      }
 >
 >  It is pretty clear that:
 >  a) this does not deal with the case of step or next
 >  b) this does not work in the case of continue often
 >     times because step will be set to 1
 >  and hence, this code does _not_ work around the bug.
 >
 >  This appears to be less of a GDB bug and more of a
 >  kernel bug in ptrace.
 
 There are at least 3 bugs, and the enclosed patches fix 2 of them.
 
 (1) sendsig() didn't clear the trace flag.  This was the primary cause
     of the misbehaviour reported in the PR.  When gdb restarts the
     process, there is a SIGALRM pending unless you type _very_ fast.
     gdb sets the trace flag before restarting the process, and the
     bug causes the signal handler to inherit the trace flag, so the
     signal handler stops after its first instruction.
 
 (2) syscall() sometimes generates a SIGTRAP, and got it wrong.  When
     the trace flag is set for an instruction that traps to the kernel
     for a syscall, syscall() wants to generate a SIGTRAP for the first
     instruction after the syscall returns instead of 1 later.  It got
     this wrong by checking the new trace flag instead of the old one.
     This bites when the sigreturn() is from a signal handler and the
     signal handler interrupted an instruction that is being traced.
     With the fix for (1), the trace flag will be off until the signal
     handler calls sigreturn().  Bug (2) causes generation of a SIGTRAP
     for the instruction that is being traced before that instruction
     can be started, and this trap repeats unless you type faster than
     the signals arrive (or type ahead).
 
 (3) "stepi" seems to work perfectly with these fixes for (1) and (2), but
     "next" rarely works.  gdb apparently gets confused about the temporary
     breakpoints that it sets for "next".  It gets SIGTRAPs for them and
     should remove them and continue with single steps, but it leaves them
     in and spins getting SIGTRAPs (and SIGALRMs in the test program).  This
     is with gdb-4.18.
 
 %%%
 Index: machdep.c
 ===================================================================
 RCS file: /home/ncvs/src/sys/i386/i386/machdep.c,v
 retrieving revision 1.486
 diff -u -2 -r1.486 machdep.c
 --- machdep.c	20 Dec 2001 23:48:29 -0000	1.486
 +++ machdep.c	5 Jan 2002 07:18:13 -0000
 @@ -402,5 +402,5 @@
 
  		/* See sendsig() for comments. */
 -		tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP);
 +		tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP);
  	}
 
 @@ -418,4 +418,5 @@
  	regs->tf_esp = (int)fp;
  	regs->tf_eip = PS_STRINGS - szosigcode;
 +	regs->tf_eflags &= ~PSL_T;
  	regs->tf_cs = _ucodesel;
  	regs->tf_ds = _udatasel;
 @@ -546,8 +547,4 @@
 
  		/*
 -		 * We should never have PSL_T set when returning from vm86
 -		 * mode.  It may be set here if we deliver a signal before
 -		 * getting to vm86 mode, so turn it off.
 -		 *
  		 * Clear PSL_NT to inhibit T_TSSFLT faults on return from
  		 * syscalls made by the signal handler.  This just avoids
 @@ -556,5 +553,5 @@
  		 * almost legitimately in probes for old cpu types.
  		 */
 -		tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP);
 +		tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_VIF | PSL_VIP);
  	}
 
 @@ -572,4 +569,5 @@
  	regs->tf_esp = (int)sfp;
  	regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode);
 +	regs->tf_eflags &= ~PSL_T;
  	regs->tf_cs = _ucodesel;
  	regs->tf_ds = _udatasel;
 Index: trap.c
 ===================================================================
 RCS file: /home/ncvs/src/sys/i386/i386/trap.c,v
 retrieving revision 1.210
 diff -u -2 -r1.210 trap.c
 --- trap.c	30 Dec 2001 19:43:59 -0000	1.210
 +++ trap.c	5 Jan 2002 06:19:12 -0000
 @@ -934,4 +934,5 @@
  	struct thread *td = curthread;
  	struct proc *p = td->td_proc;
 +	register_t orig_tf_eflags;
  	u_int sticks;
  	int error;
 @@ -951,4 +952,5 @@
  #endif
 
 +	orig_tf_eflags = frame.tf_eflags;
  	sticks = td->td_kse->ke_sticks;
  	td->td_frame = &frame;
 @@ -1066,5 +1068,5 @@
  	 * Traced syscall.
  	 */
 -	if ((frame.tf_eflags & PSL_T) && !(frame.tf_eflags & PSL_VM)) {
 +	if ((orig_tf_eflags & PSL_T) && !(orig_tf_eflags & PSL_VM)) {
  		frame.tf_eflags &= ~PSL_T;
  		trapsignal(p, SIGTRAP, 0);
 %%%
 
 Bruce
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200201050800.g05808m62326>