summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2015-10-23 01:27:44 +0000
committerjhb <jhb@FreeBSD.org>2015-10-23 01:27:44 +0000
commite25ab6846b2221872ca3abfa3c560b07b4afad9a (patch)
tree2210ab7fe1a3b71f90eb6a5150125774d10210ca
parenta372d7e311087ffe7087caf4f637bdcea03e8dd6 (diff)
downloadFreeBSD-src-e25ab6846b2221872ca3abfa3c560b07b4afad9a.zip
FreeBSD-src-e25ab6846b2221872ca3abfa3c560b07b4afad9a.tar.gz
MFC 287386,288949,288993:
Export current system call code and argument count for system call entry and exit events. To preserve the ABI, the new fields are moved to the end of struct thread in these branches (unlike HEAD) and explicitly copied when new threads are created. In addition, the new tests are only added in 10. r287386: Export current system call code and argument count for system call entry and exit events. procfs stop events for system call tracing report these values (argument count for system call entry and code for system call exit), but ptrace() does not provide this information. (Note that while the system call code can be determined in an ABI-specific manner during system call entry, it is not generally available during system call exit.) The values are exported via new fields at the end of struct ptrace_lwpinfo available via PT_LWPINFO. r288949: Fix various edge cases related to system call tracing. - Always set td_dbg_sc_* when P_TRACED is set on system call entry even if the debugger is not tracing system call entries. This ensures the fields are valid when reporting other stops that occur at system call boundaries such as for PT_FOLLOW_FORKS or when only tracing system call exits. - Set TDB_SCX when reporting the stop for a new child process in fork_return(). This causes the event to be reported as a system call exit. - Report a system call exit event in fork_return() for new threads in a traced process. - Copy td_dbg_sc_* to new threads instead of zeroing. This ensures that td_dbg_sc_code in particular will report the system call that created the new thread or process when it reports a system call exit event in fork_return(). - Add new ptrace tests to verify that new child processes and threads report system call exit events with a valid pl_syscall_code via PT_LWPINFO. r288993: Document the recently added pl_syscall_* fields in struct ptrace_lwpinfo.
-rw-r--r--lib/libc/sys/ptrace.225
-rw-r--r--sys/kern/kern_fork.c21
-rw-r--r--sys/kern/kern_thr.c2
-rw-r--r--sys/kern/subr_syscall.c11
-rw-r--r--sys/kern/sys_process.c11
-rw-r--r--sys/sys/proc.h4
-rw-r--r--sys/sys/ptrace.h2
-rw-r--r--tests/sys/kern/Makefile1
-rw-r--r--tests/sys/kern/ptrace_test.c259
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());
}
OpenPOWER on IntegriCloud