* 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