Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 21 Feb 2002 02:26:22 -0800 (PST)
From:      Peter Edwards <peter.edwards@openet-telecom.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/35175: ptrace(PT_DETACH, ....) doesn't do signals as advertised
Message-ID:  <200202211026.g1LAQMl55184@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         35175
>Category:       kern
>Synopsis:       ptrace(PT_DETACH, ....) doesn't do signals as advertised
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Feb 21 02:30:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator:     Peter Edwards
>Release:        4.5-STABLE
>Organization:
>Environment:
FreeBSD rocklobster 4.5-STABLE FreeBSD 4.5-STABLE #21: Wed Feb 20 11:59:40 GMT 2002     petere@rocklobster:/pub/FreeBSD/work/src/sys/compile/ROCKLOBSTER  i386

>Description:
I brought this up on -hackers, but no one seemed interested. I'll preserve it here for posterity :-)
      
I noticed that PT_DETACH() isn't working as documented: The signal argument is effectively being ignored, and a combination of 
ptrace(PT_ATTACH, pid, 0, 0)
	followed by
ptrace(PT_DETACH, pid, (caddr_t)1, 0)

results in the target process getting stopped.

This seems to be less than satisfactory: I would have thought the
ptrace() call should be capable of at least attaching and detaching
from the process without causing such spurious signals to be delivered
to its victim. It also disagrees with the documentation: Even if you
attempt to send a signal on PT_DETACH, it's not sent, and the stop
still happens

This apparent mis-handling of this signal argument to ptrace() appears
to occur in issignal(): (Im discussing line numbers from kern_sig.c,
1.72.2.14)

On ptrace(PT_ATTACH, ...), the target process gets to the stop() on
line 1254, with p->p_xstat == SIGSTOP.

On ptrace(PT_DETACH, pid, (caddr_t)1, mySig), the parent kicks the
stopped child, after clearing the P_TRACE flag.

The child wakes up, with the new signal, mySig, set in p->p_xstat, and
the old signal still present in p->p_siglist from the "psignal()" call
on line 1252. It then notices that the P_TRACED flag has been switched
off, and starts all over again, discarding the new signal, and
re-finding the original SSTOP that was sent by the ptrace(PT_ATTACH)
call. ie, the process gets sent the signal that the ptrace() call 
explicitly tried to replace.

My understanding of the issues is probably incomplete, but I've
attached a patch that fixes this by moving the check for the dropping
of PT_ATTACH until after the old signal is removed and the new signal
is inserted into p->p_siglist.

>How-To-Repeat:
Write a program that calls
	ptrace(PT_ATTACH, pid, 0, 0);
and then
	ptrace(PT_DETACH, pid, (caddr_t)1, 0);

The victim, pid, will end up stopped.



>Fix:
Index: kern_sig.c
===================================================================
RCS file: /pub/FreeBSD/development/FreeBSD-CVS/src/sys/kern/kern_sig.c,v
retrieving revision 1.72.2.14
diff -u -r1.72.2.14 kern_sig.c
--- kern_sig.c  14 Dec 2001 03:05:32 -0000      1.72.2.14
+++ kern_sig.c  12 Feb 2002 09:22:52 -0000
@@ -1257,14 +1257,6 @@
                                 && p->p_flag & P_TRACED);

                        /*
-                        * If the traced bit got turned off, go back up
-                        * to the top to rescan signals.  This ensures
-                        * that p_sig* and ps_sigact are consistent.
-                        */
-                       if ((p->p_flag & P_TRACED) == 0)
-                               continue;
-
-                       /*
                         * If parent wants us to take the signal,
                         * then it will leave it in p->p_xstat;
                         * otherwise we just look for signals again.
@@ -1275,10 +1267,21 @@
                                continue;

                        /*
-                        * Put the new signal into p_siglist.  If the
-                        * signal is being masked, look for other signals.
+                        * Put the new signal into p_siglist.
                         */
                        SIGADDSET(p->p_siglist, sig);
+
+                       /*
+                        * If the traced bit got turned off, go back up
+                        * to the top to rescan signals.  This ensures
+                        * that p_sig* and ps_sigact are consistent.
+                        */
+                       if ((p->p_flag & P_TRACED) == 0)
+                               continue;
+                       /*
+                        * If the signal is being masked, look for other
+                        * signals.
+                        */
                        if (SIGISMEMBER(p->p_sigmask, sig))
                                continue;
                }
>Release-Note:
>Audit-Trail:
>Unformatted:

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?200202211026.g1LAQMl55184>