diff options
-rw-r--r-- | lib/libc/sys/ptrace.2 | 25 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 21 | ||||
-rw-r--r-- | sys/kern/kern_thr.c | 2 | ||||
-rw-r--r-- | sys/kern/subr_syscall.c | 11 | ||||
-rw-r--r-- | sys/kern/sys_process.c | 11 | ||||
-rw-r--r-- | sys/sys/proc.h | 4 | ||||
-rw-r--r-- | sys/sys/ptrace.h | 2 | ||||
-rw-r--r-- | tests/sys/kern/Makefile | 1 | ||||
-rw-r--r-- | tests/sys/kern/ptrace_test.c | 259 |
9 files changed, 314 insertions, 22 deletions
diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 index 71b432f..5c93438 100644 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -2,7 +2,7 @@ .\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $ .\" .\" This file is in the public domain. -.Dd July 3, 2015 +.Dd October 6, 2015 .Dt PTRACE 2 .Os .Sh NAME @@ -307,6 +307,8 @@ struct ptrace_lwpinfo { siginfo_t pl_siginfo; char pl_tdname[MAXCOMLEN + 1]; int pl_child_pid; + u_int pl_syscall_code; + u_int pl_syscall_narg; }; .Ed .Pp @@ -395,6 +397,27 @@ stop when .Dv PL_FLAG_FORKED is set in .Va pl_flags . +.It pl_syscall_code +The ABI-specific identifier of the current system call. +Note that for indirect system calls this field reports the indirected +system call. +Only valid when +.Dv PL_FLAG_SCE +or +.Dv PL_FLAG_SCX +is set in +.Va pl_flags. +.It pl_syscall_narg +The number of arguments passed to the current system call not counting +the system call identifier. +Note that for indirect system calls this field reports the arguments +passed to the indirected system call. +Only valid when +.Dv PL_FLAG_SCE +or +.Dv PL_FLAG_SCX +is set in +.Va pl_flags. .El .It PT_GETNUMLWPS This request returns the number of kernel threads associated with the diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 10b2339..99451c0 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/procdesc.h> #include <sys/pioctl.h> +#include <sys/ptrace.h> #include <sys/racct.h> #include <sys/resourcevar.h> #include <sys/sched.h> @@ -478,6 +479,8 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, td2->td_sigstk = td->td_sigstk; td2->td_flags = TDF_INMEM; td2->td_lend_user_pri = PRI_MAX; + td2->td_dbg_sc_code = td->td_dbg_sc_code; + td2->td_dbg_sc_narg = td->td_dbg_sc_narg; #ifdef VIMAGE td2->td_vnet = NULL; @@ -1049,8 +1052,8 @@ fork_return(struct thread *td, struct trapframe *frame) { struct proc *p, *dbg; + p = td->td_proc; if (td->td_dbgflags & TDB_STOPATFORK) { - p = td->td_proc; sx_xlock(&proctree_lock); PROC_LOCK(p); if ((p->p_pptr->p_flag & (P_TRACED | P_FOLLOWFORK)) == @@ -1067,9 +1070,9 @@ fork_return(struct thread *td, struct trapframe *frame) p->p_pid, p->p_oppid); proc_reparent(p, dbg); sx_xunlock(&proctree_lock); - td->td_dbgflags |= TDB_CHILD; + td->td_dbgflags |= TDB_CHILD | TDB_SCX; ptracestop(td, SIGSTOP); - td->td_dbgflags &= ~TDB_CHILD; + td->td_dbgflags &= ~(TDB_CHILD | TDB_SCX); } else { /* * ... otherwise clear the request. @@ -1079,6 +1082,18 @@ fork_return(struct thread *td, struct trapframe *frame) cv_broadcast(&p->p_dbgwait); } PROC_UNLOCK(p); + } else if (p->p_flag & P_TRACED) { + /* + * This is the start of a new thread in a traced + * process. Report a system call exit event. + */ + PROC_LOCK(p); + td->td_dbgflags |= TDB_SCX; + _STOPEVENT(p, S_SCX, td->td_dbg_sc_code); + if ((p->p_stops & S_PT_SCX) != 0) + ptracestop(td, SIGTRAP); + td->td_dbgflags &= ~TDB_SCX; + PROC_UNLOCK(p); } userret(td, frame); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index bcbc3d0..058dc19 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -240,6 +240,8 @@ thread_create(struct thread *td, struct rtprio *rtp, __rangeof(struct thread, td_startcopy, td_endcopy)); newtd->td_proc = td->td_proc; newtd->td_ucred = crhold(td->td_ucred); + newtd->td_dbg_sc_code = td->td_dbg_sc_code; + newtd->td_dbg_sc_narg = td->td_dbg_sc_narg; error = initialize_thread(newtd, thunk); if (error != 0) { diff --git a/sys/kern/subr_syscall.c b/sys/kern/subr_syscall.c index 925d732..1ad7dd0 100644 --- a/sys/kern/subr_syscall.c +++ b/sys/kern/subr_syscall.c @@ -84,9 +84,12 @@ syscallenter(struct thread *td, struct syscall_args *sa) if (error == 0) { STOPEVENT(p, S_SCE, sa->narg); - if (p->p_flag & P_TRACED && p->p_stops & S_PT_SCE) { + if (p->p_flag & P_TRACED) { PROC_LOCK(p); - ptracestop((td), SIGTRAP); + td->td_dbg_sc_code = sa->code; + td->td_dbg_sc_narg = sa->narg; + if (p->p_stops & S_PT_SCE) + ptracestop((td), SIGTRAP); PROC_UNLOCK(p); } if (td->td_dbgflags & TDB_USERWR) { @@ -95,6 +98,10 @@ syscallenter(struct thread *td, struct syscall_args *sa) * debugger modified registers or memory. */ error = (p->p_sysent->sv_fetch_syscall_args)(td, sa); + PROC_LOCK(p); + td->td_dbg_sc_code = sa->code; + td->td_dbg_sc_narg = sa->narg; + PROC_UNLOCK(p); #ifdef KTRACE if (KTRPOINT(td, KTR_SYSCALL)) ktrsyscall(sa->code, sa->narg, sa->args); diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 123dd10..15adf9a 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -96,6 +96,8 @@ struct ptrace_lwpinfo32 { struct siginfo32 pl_siginfo; /* siginfo for signal */ char pl_tdname[MAXCOMLEN + 1]; /* LWP name. */ int pl_child_pid; /* New child pid */ + u_int pl_syscall_code; + u_int pl_syscall_narg; }; #endif @@ -480,6 +482,8 @@ ptrace_lwpinfo_to32(const struct ptrace_lwpinfo *pl, siginfo_to_siginfo32(&pl->pl_siginfo, &pl32->pl_siginfo); strcpy(pl32->pl_tdname, pl->pl_tdname); pl32->pl_child_pid = pl->pl_child_pid; + pl32->pl_syscall_code = pl->pl_syscall_code; + pl32->pl_syscall_narg = pl->pl_syscall_narg; } #endif /* COMPAT_FREEBSD32 */ @@ -1210,6 +1214,13 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) pl->pl_sigmask = td2->td_sigmask; pl->pl_siglist = td2->td_siglist; strcpy(pl->pl_tdname, td2->td_name); + if ((td2->td_dbgflags & (TDB_SCE | TDB_SCX)) != 0) { + pl->pl_syscall_code = td2->td_dbg_sc_code; + pl->pl_syscall_narg = td2->td_dbg_sc_narg; + } else { + pl->pl_syscall_code = 0; + pl->pl_syscall_narg = 0; + } #ifdef COMPAT_FREEBSD32 if (wrap32) ptrace_lwpinfo_to32(pl, pl32); diff --git a/sys/sys/proc.h b/sys/sys/proc.h index b9606f8..3b188ca 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -170,6 +170,7 @@ struct procdesc; struct racct; struct sbuf; struct sleepqueue; +struct syscall_args; struct td_sched; struct thread; struct trapframe; @@ -320,6 +321,8 @@ struct thread { struct vm_page **td_ma; /* (k) uio pages held */ int td_ma_cnt; /* (k) size of *td_ma */ void *td_su; /* (k) FFS SU private */ + u_int td_dbg_sc_code; /* (c) Syscall code to debugger. */ + u_int td_dbg_sc_narg; /* (c) Syscall arg count to debugger.*/ }; struct mtx *thread_lock_block(struct thread *); @@ -934,7 +937,6 @@ void userret(struct thread *, struct trapframe *); void cpu_exit(struct thread *); void exit1(struct thread *, int) __dead2; -struct syscall_args; int cpu_fetch_syscall_args(struct thread *td, struct syscall_args *sa); void cpu_fork(struct thread *, struct proc *, struct thread *, int); void cpu_set_fork_handler(struct thread *, void (*)(void *), void *); diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h index e770a06..de4e7a7 100644 --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -113,6 +113,8 @@ struct ptrace_lwpinfo { struct __siginfo pl_siginfo; /* siginfo for signal */ char pl_tdname[MAXCOMLEN + 1]; /* LWP name */ int pl_child_pid; /* New child pid */ + u_int pl_syscall_code; + u_int pl_syscall_narg; }; /* Argument structure for PT_VM_ENTRY. */ diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index bf6aa0d..c345e5d 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -7,6 +7,7 @@ ATF_TESTS_C+= ptrace_test ATF_TESTS_C+= unix_seqpacket_test TEST_METADATA.unix_seqpacket_test+= timeout="15" +LDADD.ptrace_test+= -lpthread LDADD.unix_seqpacket_test+= -lpthread WARNS?= 5 diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c index c10c097..1731698 100644 --- a/tests/sys/kern/ptrace_test.c +++ b/tests/sys/kern/ptrace_test.c @@ -29,10 +29,12 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <sys/ptrace.h> +#include <sys/syscall.h> #include <sys/sysctl.h> #include <sys/user.h> #include <sys/wait.h> #include <errno.h> +#include <pthread.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -409,12 +411,15 @@ ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc) * debugger is attached to it. */ static __dead2 void -follow_fork_parent(void) +follow_fork_parent(bool use_vfork) { pid_t fpid, wpid; int status; - CHILD_REQUIRE((fpid = fork()) != -1); + if (use_vfork) + CHILD_REQUIRE((fpid = vfork()) != -1); + else + CHILD_REQUIRE((fpid = fork()) != -1); if (fpid == 0) /* Child */ @@ -434,7 +439,7 @@ follow_fork_parent(void) * child process. */ static pid_t -handle_fork_events(pid_t parent) +handle_fork_events(pid_t parent, struct ptrace_lwpinfo *ppl) { struct ptrace_lwpinfo pl; bool fork_reported[2]; @@ -469,6 +474,8 @@ handle_fork_events(pid_t parent) child = wpid; else ATF_REQUIRE(child == wpid); + if (ppl != NULL) + ppl[1] = pl; fork_reported[1] = true; } else { ATF_REQUIRE(wpid == parent); @@ -478,6 +485,8 @@ handle_fork_events(pid_t parent) child = pl.pl_child_pid; else ATF_REQUIRE(child == pl.pl_child_pid); + if (ppl != NULL) + ppl[0] = pl; fork_reported[0] = true; } } @@ -499,7 +508,7 @@ ATF_TC_BODY(ptrace__follow_fork_both_attached, tc) ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); - follow_fork_parent(); + follow_fork_parent(false); } /* Parent process. */ @@ -516,7 +525,7 @@ ATF_TC_BODY(ptrace__follow_fork_both_attached, tc) /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); - children[1] = handle_fork_events(children[0]); + children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); @@ -555,7 +564,7 @@ ATF_TC_BODY(ptrace__follow_fork_child_detached, tc) ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); - follow_fork_parent(); + follow_fork_parent(false); } /* Parent process. */ @@ -572,7 +581,7 @@ ATF_TC_BODY(ptrace__follow_fork_child_detached, tc) /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); - children[1] = handle_fork_events(children[0]); + children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); @@ -606,7 +615,7 @@ ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc) ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); - follow_fork_parent(); + follow_fork_parent(false); } /* Parent process. */ @@ -623,7 +632,7 @@ ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc) /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); - children[1] = handle_fork_events(children[0]); + children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); @@ -688,7 +697,7 @@ ATF_TC_BODY(ptrace__follow_fork_both_attached_unrelated_debugger, tc) ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); - follow_fork_parent(); + follow_fork_parent(false); } /* Parent process. */ @@ -715,7 +724,7 @@ ATF_TC_BODY(ptrace__follow_fork_both_attached_unrelated_debugger, tc) /* Signal the fork parent to continue. */ close(cpipe[0]); - children[1] = handle_fork_events(children[0]); + children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); @@ -756,7 +765,7 @@ ATF_TC_BODY(ptrace__follow_fork_child_detached_unrelated_debugger, tc) ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); - follow_fork_parent(); + follow_fork_parent(false); } /* Parent process. */ @@ -783,7 +792,7 @@ ATF_TC_BODY(ptrace__follow_fork_child_detached_unrelated_debugger, tc) /* Signal the fork parent to continue. */ close(cpipe[0]); - children[1] = handle_fork_events(children[0]); + children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); @@ -819,7 +828,7 @@ ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc) ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); - follow_fork_parent(); + follow_fork_parent(false); } /* Parent process. */ @@ -846,7 +855,7 @@ ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc) /* Signal the fork parent to continue. */ close(cpipe[0]); - children[1] = handle_fork_events(children[0]); + children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); @@ -866,6 +875,223 @@ ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc) ATF_REQUIRE(errno == ECHILD); } +/* + * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new + * child process created via fork() reports the correct value. + */ +ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_fork); +ATF_TC_BODY(ptrace__new_child_pl_syscall_code_fork, tc) +{ + struct ptrace_lwpinfo pl[2]; + pid_t children[2], fpid, wpid; + int status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + trace_me(); + follow_fork_parent(false); + } + + /* Parent process. */ + children[0] = fpid; + + /* The first wait() should report the stop from SIGSTOP. */ + wpid = waitpid(children[0], &status, 0); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); + + /* Continue the child ignoring the SIGSTOP. */ + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + + /* Wait for both halves of the fork event to get reported. */ + children[1] = handle_fork_events(children[0], pl); + ATF_REQUIRE(children[1] > 0); + + ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0); + ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); + ATF_REQUIRE(pl[0].pl_syscall_code == SYS_fork); + ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code); + ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg); + + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); + + /* + * The child can't exit until the grandchild reports status, so the + * grandchild should report its exit first to the debugger. + */ + wpid = wait(&status); + ATF_REQUIRE(wpid == children[1]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 2); + + wpid = wait(&status); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + +/* + * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new + * child process created via vfork() reports the correct value. + */ +ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_vfork); +ATF_TC_BODY(ptrace__new_child_pl_syscall_code_vfork, tc) +{ + struct ptrace_lwpinfo pl[2]; + pid_t children[2], fpid, wpid; + int status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + trace_me(); + follow_fork_parent(true); + } + + /* Parent process. */ + children[0] = fpid; + + /* The first wait() should report the stop from SIGSTOP. */ + wpid = waitpid(children[0], &status, 0); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); + + /* Continue the child ignoring the SIGSTOP. */ + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + + /* Wait for both halves of the fork event to get reported. */ + children[1] = handle_fork_events(children[0], pl); + ATF_REQUIRE(children[1] > 0); + + ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0); + ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); + ATF_REQUIRE(pl[0].pl_syscall_code == SYS_vfork); + ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code); + ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg); + + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); + + /* + * The child can't exit until the grandchild reports status, so the + * grandchild should report its exit first to the debugger. + */ + wpid = wait(&status); + ATF_REQUIRE(wpid == children[1]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 2); + + wpid = wait(&status); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + +static void * +simple_thread(void *arg __unused) +{ + + pthread_exit(NULL); +} + +/* + * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new + * thread reports the correct value. + */ +ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_thread); +ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc) +{ + struct ptrace_lwpinfo pl; + pid_t fpid, wpid; + lwpid_t main; + int status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + pthread_t thread; + + trace_me(); + + CHILD_REQUIRE(pthread_create(&thread, NULL, simple_thread, + NULL) == 0); + CHILD_REQUIRE(pthread_join(thread, NULL) == 0); + exit(1); + } + + /* The first wait() should report the stop from SIGSTOP. */ + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(wpid == fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, + sizeof(pl)) != -1); + main = pl.pl_lwpid; + + /* + * Continue the child ignoring the SIGSTOP and tracing all + * system call exits. + */ + ATF_REQUIRE(ptrace(PT_TO_SCX, fpid, (caddr_t)1, 0) != -1); + + /* + * Wait for the new thread to arrive. pthread_create() might + * invoke any number of system calls. For now we just wait + * for the new thread to arrive and make sure it reports a + * valid system call code. If ptrace grows thread event + * reporting then this test can be made more precise. + */ + for (;;) { + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(wpid == fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); + + ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, + sizeof(pl)) != -1); + ATF_REQUIRE((pl.pl_flags & PL_FLAG_SCX) != 0); + ATF_REQUIRE(pl.pl_syscall_code != 0); + if (pl.pl_lwpid != main) + /* New thread seen. */ + break; + + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); + } + + /* Wait for the child to exit. */ + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); + for (;;) { + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(wpid == fpid); + if (WIFEXITED(status)) + break; + + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); + } + + ATF_REQUIRE(WEXITSTATUS(status) == 1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + ATF_TP_ADD_TCS(tp) { @@ -881,6 +1107,9 @@ ATF_TP_ADD_TCS(tp) ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached_unrelated_debugger); + ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_fork); + ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_vfork); + ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread); return (atf_no_error()); } |