Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 Apr 2010 08:51:56 -0400
From:      John Baldwin <jhb@freebsd.org>
To:        freebsd-hackers@freebsd.org
Cc:        Gunnar Hinriksson <tomtinn@gmail.com>, bde@freebsd.org
Subject:   Re: Ptrace segfault
Message-ID:  <201004300851.56399.jhb@freebsd.org>
In-Reply-To: <w2lcbb19c781004291649p96fb1c68scef0bce53ea253b3@mail.gmail.com>
References:  <q2vcbb19c781004291206sc54fdb6ag53c3a763ad364e8e@mail.gmail.com> <m2wcbb19c781004291636rd7d5709dr81563f4ce6f4bf25@mail.gmail.com> <w2lcbb19c781004291649p96fb1c68scef0bce53ea253b3@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thursday 29 April 2010 7:49:26 pm Gunnar Hinriksson wrote:
> 2010/4/29 Gunnar Hinriksson <tomtinn@gmail.com>:
> > 2010/4/29 Gunnar Hinriksson <tomtinn@gmail.com>:
> >> 2010/4/29 Bob Bishop <rb@gid.co.uk>:
> >>> Hi,
> >>>
> >>> On 29 Apr 2010, at 22:37, Garrett Cooper wrote:
> >>>
> >>>> On Thu, Apr 29, 2010 at 12:06 PM, Gunnar Hinriksson <tomtinn@gmail.com> 
wrote:
> >>>>> Hello
> >>>>>
> >>>>> Im having a little problem using ptrace on my system.
> >>>>> If I use ptrace to attach to another process the child process
> >>>>> segfaults once I detach.
> >>>>> For example using this simple program.
> >>>>>
> >>>>> #include <stdio.h>
> >>>>> #include <stdlib.h>
> >>>>> #include <sys/types.h>
> >>>>> #include <sys/ptrace.h>
> >>>>> #include <sys/wait.h>
> >>>>>
> >>>>> int main(int argc, char *argv[])
> >>>>> {
> >>>>>        int pid = atoi(argv[1]);
> >>>>>        ptrace(PT_ATTACH, pid, 0, 0);
> >>>>>        wait(NULL);
> >>>>>        ptrace(PT_DETACH, pid, 0, 0);
> >>>>>        return 0;
> >>>>> }
> >>>>>
> >>>>> Am I using ptrace incorrectly or is there perhaps a bug in ptrace that
> >>>>> causes the child to always segfault ?
> >>>>
> >>>>    Nope -- it's a bug in your code. From ptrace(2):
> >>>>
> >>>>     PT_CONTINUE   The traced process continues execution.  The addr 
argument
> >>>>                   is an address specifying the place where execution is 
to be
> >>>>                   resumed (a new value for the program counter), or
> >>>>                   (caddr_t)1 to indicate that execution is to pick up 
where
> >>>>                   it left off.  The data argument provides a signal 
number to
> >>>>                   be delivered to the traced process as it resumes 
execution,
> >>>>                   or 0 if no signal is to be sent.
> >>>>
> >>>> [...]
> >>>>
> >>>>     PT_DETACH     This request is like PT_CONTINUE, except that it does 
not
> >>>                                                                 
^^^^^^^^^^^
> >>>>                   allow specifying an alternate place to continue 
execution,
> >>>                   
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> >>>>                   and after it succeeds, the traced process is no 
longer
> >>>>                   traced and continues execution normally.
> >>>>
> >>>>    Note very carefully the fact that PT_DETACH is like PT_CONTINUE,
> >>>> and that PT_CONTINUE says that addr references the memory where the
> >>>> execution is going to be resumed.
> >>>
> >>> Looks to me like a bug in ptrace(PT_DETACH,...) which to agree with 
ptrace(2) ought either to
> >>> (a) fail (EINVAL?) if addr != (caddr_t)1, or
> >>> (b) ignore addr entirely; it's not clear which.
> >>>
> >>> OP inferred (b) which is reasonable.
> >>>
> >>>> HTH,
> >>>> -Garrett
> >>>> _______________________________________________
> >>>> freebsd-hackers@freebsd.org mailing list
> >>>> http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
> >>>> To unsubscribe, send any mail to "freebsd-hackers-
unsubscribe@freebsd.org"
> >>>>
> >>>>
> >>>
> >>>
> >>> --
> >>> Bob Bishop          +44 (0)118 940 1243
> >>> rb@gid.co.uk    fax +44 (0)118 940 1295
> >>>             mobile +44 (0)783 626 4518
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>
> >> Hello
> >>
> >> I didn't want to make the code to big so I omitted any tests.
> >> About wait(), you are supposed to use wait to check if the child
> >> process has stopped successfully.
> >> I guess the correct usage would be something like this if im
> >> understanding the documentation correctly.
> >> if ( wait(WIFSTOPPED(SIGSTOP)) == pid )
> >> Im not sure if there is any posix way of describing how ptrace should
> >> be implemented but I know linux ptrace ignores the addr on DETACH.
> >> So my vote would be that ptrace ignores addr on detach to make it
> >> compatible with code for linux.
> >>
> >> With Regards
> >>
> >> Gunnar
> >>
> >
> > Hello
> >
> > I started looking around in the kernel sources and I think i've found
> > out how it is implemented.
> > in /usr/src/sys/kern/sys_process.c line 744 the following code is found.
> >
> > if (addr != (void *)1) {
> >                        error = ptrace_set_pc(td2, (u_long)
(uintfptr_t)addr);
> >                        if (error)
> >                                break;
> >                }
> >
> > So it is changing the address if the addr is anything but 1.
> > I tested my program by passing 1 as an argument in the addr field and
> > the segfaults stopped.
> >
> > So the question is if this code should be removed so that PT_DETACH
> > ignores addr or leave it be and perhaps update the documentation to
> > reflect that it allows the addr to the changed.
> >
> > With Regards
> >
> > Gunnar
> >
> 
> After reading the code more carefully the correct way would be to
> modify it like this.
> ---
> case PT_CONTINUE:
> {
>  if (addr != (void *)1) {
>                         error = ptrace_set_pc(td2, (u_long)
(uintfptr_t)addr);
>                 }
> }
> if (error)
> 	break;
> ---
> Note that im just learning programming so this might not be the
> correct way to do it.

I think this might be the cleanest.  None of the other commands here need to 
adjust the pc (PT_STEP requires that addr be '1' in the manpage for example), 
so this change cleans up the logic some by only trying to set the pc for 
PT_CONTINUE.  It also moves some PT_DETACH logic up into the switch as a 
PT_DETACH case as well.

Index: sys_process.c
===================================================================
--- sys_process.c	(revision 207329)
+++ sys_process.c	(working copy)
@@ -899,6 +899,14 @@
 			if (error)
 				goto out;
 			break;
+		case PT_CONTINUE:
+			if (addr != (void *)1) {
+				error = ptrace_set_pc(td2,
+				    (u_long)(uintfptr_t)addr);
+				if (error)
+					goto out;
+			}
+			break;
 		case PT_TO_SCE:
 			p->p_stops |= S_PT_SCE;
 			break;
@@ -908,15 +916,7 @@
 		case PT_SYSCALL:
 			p->p_stops |= S_PT_SCE | S_PT_SCX;
 			break;
-		}
-
-		if (addr != (void *)1) {
-			error = ptrace_set_pc(td2, (u_long)(uintfptr_t)addr);
-			if (error)
-				break;
-		}
-
-		if (req == PT_DETACH) {
+		case PT_DETACH:
 			/* reset process parent */
 			if (p->p_oppid != p->p_pptr->p_pid) {
 				struct proc *pp;
@@ -941,6 +941,7 @@
 
 			/* should we send SIGCHLD? */
 			/* childproc_continued(p); */
+			break;
 		}
 
 	sendsig:

-- 
John Baldwin



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