* defs.h [LINUX] (TCP_FORK_EVENT): New flag.
* defs.h [LINUX] (internal_fork_child): New argument: from_ptrace_event.
* process.c [LINUX] (internal_fork_child): Ditto.
* process.c [LINUX] (internal_fork): If TCP_FORK_EVENT is set, don't
attach to the child using the syscall return value.

Signed-off-by: Jamie Lokier <ja...@shareable.org>
---
 defs.h    |    5 +++--
 process.c |   51 ++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 47 insertions(+), 9 deletions(-)

diff --git a/defs.h b/defs.h
index ab69a45..fadbd92 100644
--- a/defs.h
+++ b/defs.h
@@ -403,11 +403,12 @@ struct tcb {
   || defined(POWERPC) || defined(IA64) || defined(HPPA) \
   || defined(SH) || defined(SH64) || defined(S390) || defined(S390X) \
   || defined(ARM) || defined(MIPS) || defined(BFIN) || defined(TILE)
-#  define TCB_WAITEXECVE 02000 /* ignore SIGTRAP after exceve */
+#  define TCB_WAITEXECVE 02000    /* Ignore SIGTRAP after exceve */
 # endif
 # define TCB_CLONE_DETACHED 04000 /* CLONE_DETACHED set in creating syscall */
 # define TCB_CLONE_THREAD  010000 /* CLONE_THREAD set in creating syscall */
 # define TCB_GROUP_EXITING 020000 /* TCB_EXITING was exit_group, not _exit */
+# define TCB_FORK_EVENT    040000 /* Got PTRACE_EVENT_{FORK,VFORK,CLONE} */
 # include <sys/syscall.h>
 # ifndef __NR_exit_group
 # /* Hack: Most headers around are too old to have __NR_exit_group.  */
@@ -566,7 +567,7 @@ extern int is_restart_error(struct tcb *);
 
 extern int change_syscall(struct tcb *, int);
 #ifdef LINUX
-extern int internal_fork_child(struct tcb *, int);
+extern int internal_fork_child(struct tcb *, int, int);
 #endif
 extern int internal_fork(struct tcb *);
 extern int internal_exec(struct tcb *);
diff --git a/process.c b/process.c
index 645f2c4..e46957d 100644
--- a/process.c
+++ b/process.c
@@ -790,11 +790,16 @@ change_syscall(struct tcb *tcp, int new)
 #ifdef LINUX
 /*
  * Called when we learn about a child process and know which parent
- * it's from.  (Dubiously, pid here is int because alloctcb() uses it,
- * but syscall return values are unsigned long and could overflow.)
+ * it's from.  (Dubiously, pid here is int
+ * because alloctcb() uses it, but syscall return values are unsigned
+ * long and could overflow.)
+ *
+ * from_ptrace_event is set if called for PTRACE_EVENT_{FORK,VFORK,CLONE}.
+ * Then we know the child is attached and tracing despite CLONE_PTRACE
+ * not being used.
  */
 int
-internal_fork_child(struct tcb *tcp, int pid)
+internal_fork_child(struct tcb *tcp, int pid, int from_ptrace_event)
 {
        struct tcb *tcpchild;
        int clone_flags;
@@ -814,6 +819,26 @@ internal_fork_child(struct tcb *tcp, int pid)
                tcpchild = alloctcb(pid);
        }
 
+       /* Set by setbpt. */
+       clone_flags = tcp->u_arg[ARG_FLAGS];
+
+       /* This handles the cases when we didn't use CLONE_PTRACE
+          because we requested PTRACE_EVENT_{FORK,VFORK,CLONE}, but
+          for some reason we didn't get the event.  It's not perfect
+          because the child isn't stopped immediately.  This should
+          never happen, but it's easy and there have been kernel
+          bugs, so let's do it. */
+       if (!from_ptrace_event && !(clone_flags & CLONE_PTRACE)) {
+               /* Linux returns EPERM if the child has exited by now. */
+               if (ptrace(PTRACE_ATTACH, pid, (char *) 1, 0) < 0) {
+                       if (tcp->flags & TCB_BPTSET)
+                               clearbpt(tcp);
+                       perror("PTRACE_ATTACH");
+                       fprintf(stderr, "Too late?\n");
+                       droptcb(tcpchild);
+                       return 0;
+               }
+       }
        tcpchild->flags |= TCB_ATTACHED;
 
        if ((tcp->flags & TCB_CLONE_THREAD) && tcp->parent != NULL) {
@@ -832,9 +857,6 @@ internal_fork_child(struct tcb *tcp, int pid)
        }
        ++(tcpchild->parent)->nchildren;
 
-       /* Set by setbpt. */
-       clone_flags = tcp->u_arg[ARG_FLAGS];
-
        if (clone_flags & CLONE_THREAD) {
                tcpchild->flags |= TCB_CLONE_THREAD;
                ++(tcpchild->parent)->nclone_threads;
@@ -902,9 +924,24 @@ internal_fork(struct tcb *tcp)
                        return 0;
                }
 
+               if (tcp->flags & TCB_FORK_EVENT) {
+                       /*
+                        * We've begun tracing the child already.
+                        * Don't use the syscall return value to attach
+                        * to the child, (a) to avoid confusion, (b)
+                        * because the child may have been traced,
+                        * finished and exited already, and (c) it's
+                        * definitely exited if this was a vfork.
+                        */
+                       tcp->flags &= ~TCB_FORK_EVENT;
+                       if (bpt)
+                               clearbpt(tcp);
+                       return 0;
+               }
+
                pid = tcp->u_rval;
 
-               return internal_fork_child(tcp, pid);
+               return internal_fork_child(tcp, pid, 0);
        }
        return 0;
 }
-- 
1.7.0.4

_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to