Module Name:    src
Committed By:   kamil
Date:           Tue May  1 16:37:23 UTC 2018

Modified Files:
        src/lib/libc/sys: ptrace.2
        src/sys/kern: kern_fork.c kern_sig.c sys_ptrace_common.c
        src/sys/sys: signalvar.h
        src/tests/lib/libc/sys: t_ptrace_wait.c

Log Message:
Implement PTRACE_VFORK

Add support for tracing vfork(2) events in the context of ptrace(2).

This API covers other frontends to fork1(9) like posix_spawn(2) or clone(2),
if they cause parent to wait for exec(2) or exit(2) of the child.

Changes:
 - Add new argument to sigswitch() determining whether we need to acquire
   the proc_lock or whether it's already held.
 - Refactor fork1(9) for fork(2) and vfork(2)-like events.
   Call sigswitch() from fork(1) for forking or vforking parent, instead of
   emitting kpsignal(9). We need to emit the signal and suspend the parent,
   returning to user and relock proc_lock.
 - Add missing prototype for proc_stop_done() in kern_sig.c.
 - Make sigswitch a public function accessible from other kernel code
   including <sys/signalvar.h>.
 - Remove an entry about unimplemented PTRACE_VFORK in the ptrace(2) man page.
 - Permin PTRACE_VFORK in the ptrace(2) frontend for userland.
 - Remove expected failure for unimplemented PTRACE_VFORK tests in the ATF
   ptrace(2) test-suite.
 - Relax signal routing constraints under a debugger for a vfork(2)ed child.
   This intended to protect from signaling a parent of a vfork(2)ed child that
   called PT_TRACE_ME, but wrongly misrouted other signals in vfork(2)
   use-cases.

Add XXX comments about still existing problems and future enhancements:
 - correct vfork(2) + PT_TRACE_ME handling.
 - fork1(2) handling of scenarios when a process is collected in valid but
   rare cases.

All ATF ptrace(2) fork[1-8] and vfork[1-8] tests pass.

Fix PR kern/51630 by Kamil Rytarowski (myself).

Sponsored by <The NetBSD Foundation>


To generate a diff of this commit:
cvs rdiff -u -r1.68 -r1.69 src/lib/libc/sys/ptrace.2
cvs rdiff -u -r1.204 -r1.205 src/sys/kern/kern_fork.c
cvs rdiff -u -r1.341 -r1.342 src/sys/kern/kern_sig.c
cvs rdiff -u -r1.39 -r1.40 src/sys/kern/sys_ptrace_common.c
cvs rdiff -u -r1.89 -r1.90 src/sys/sys/signalvar.h
cvs rdiff -u -r1.37 -r1.38 src/tests/lib/libc/sys/t_ptrace_wait.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libc/sys/ptrace.2
diff -u src/lib/libc/sys/ptrace.2:1.68 src/lib/libc/sys/ptrace.2:1.69
--- src/lib/libc/sys/ptrace.2:1.68	Mon Mar  5 11:24:35 2018
+++ src/lib/libc/sys/ptrace.2	Tue May  1 16:37:23 2018
@@ -1,7 +1,7 @@
-.\"	$NetBSD: ptrace.2,v 1.68 2018/03/05 11:24:35 kamil Exp $
+.\"	$NetBSD: ptrace.2,v 1.69 2018/05/01 16:37:23 kamil Exp $
 .\"
 .\" This file is in the public domain.
-.Dd April 7, 2017
+.Dd May 1, 2018
 .Dt PTRACE 2
 .Os
 .Sh NAME
@@ -841,10 +841,6 @@ to
 .Ec ,
 should be able to sidestep this.
 .Pp
-.Dv PTRACE_VFORK
-is currently unimplemented and it will return
-.Er ENOTSUP .
-.Pp
 .Dv PT_SET_SIGINFO ,
 .Dv PT_RESUME
 and

Index: src/sys/kern/kern_fork.c
diff -u src/sys/kern/kern_fork.c:1.204 src/sys/kern/kern_fork.c:1.205
--- src/sys/kern/kern_fork.c:1.204	Mon Apr 16 14:51:59 2018
+++ src/sys/kern/kern_fork.c	Tue May  1 16:37:23 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_fork.c,v 1.204 2018/04/16 14:51:59 kamil Exp $	*/
+/*	$NetBSD: kern_fork.c,v 1.205 2018/05/01 16:37:23 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2001, 2004, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_fork.c,v 1.204 2018/04/16 14:51:59 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_fork.c,v 1.205 2018/05/01 16:37:23 kamil Exp $");
 
 #include "opt_ktrace.h"
 #include "opt_dtrace.h"
@@ -218,7 +218,7 @@ fork1(struct lwp *l1, int flags, int exi
 	int		count;
 	vaddr_t		uaddr;
 	int		tnprocs;
-	int		tracefork, tracevforkdone;
+	int		tracefork, tracevfork, tracevforkdone;
 	int		error = 0;
 
 	p1 = l1->l_proc;
@@ -478,21 +478,19 @@ fork1(struct lwp *l1, int flags, int exi
 	 */
 	tracefork = (p1->p_slflag & (PSL_TRACEFORK|PSL_TRACED)) ==
 	    (PSL_TRACEFORK|PSL_TRACED) && (flags && FORK_PPWAIT) == 0;
+	tracevfork = (p1->p_slflag & (PSL_TRACEVFORK|PSL_TRACED)) ==
+	    (PSL_TRACEVFORK|PSL_TRACED) && (flags && FORK_PPWAIT) != 0;
 	tracevforkdone = (p1->p_slflag & (PSL_TRACEVFORK_DONE|PSL_TRACED)) ==
 	    (PSL_TRACEVFORK_DONE|PSL_TRACED) && (flags && FORK_PPWAIT);
-	if (tracefork) {
+	if (tracefork || tracevfork)
 		proc_changeparent(p2, p1->p_pptr);
-		/*
-		 * Set ptrace status.
-		 */
+	if (tracefork) {
 		p1->p_fpid = p2->p_pid;
 		p2->p_fpid = p1->p_pid;
 	}
-	if (tracevforkdone) {
-		/*
-		 * Set ptrace status.
-		 */
-		p1->p_vfpid_done = p2->p_pid;
+	if (tracevfork) {
+		p1->p_vfpid = p2->p_pid;
+		p2->p_vfpid = p1->p_pid;
 	}
 
 	LIST_INSERT_AFTER(p1, p2, p_pglist);
@@ -579,19 +577,35 @@ fork1(struct lwp *l1, int flags, int exi
 		retval[0] = p2->p_pid;
 		retval[1] = 0;
 	}
+
 	mutex_exit(p2->p_lock);
 
 	/*
+	 * Let the parent know that we are tracing its child.
+	 */
+	if (tracefork || tracevfork) {
+		mutex_enter(p1->p_lock);
+		p1->p_xsig = SIGTRAP;
+		p1->p_sigctx.ps_faked = true; // XXX
+		p1->p_sigctx.ps_info._signo = p1->p_xsig;
+		p1->p_sigctx.ps_info._code = TRAP_CHLD;
+		sigswitch(0, SIGTRAP, false);
+		// XXX ktrpoint(KTR_PSIG)
+		mutex_exit(p1->p_lock);
+		mutex_enter(proc_lock);
+	}
+
+	/*
 	 * Preserve synchronization semantics of vfork.  If waiting for
 	 * child to exec or exit, sleep until it clears LP_VFORKWAIT.
 	 */
-	while (p2->p_lflag & PL_PPWAIT)
+	while (p2->p_lflag & PL_PPWAIT) // XXX: p2 can go invalid
 		cv_wait(&p1->p_waitcv, proc_lock);
 
 	/*
 	 * Let the parent know that we are tracing its child.
 	 */
-	if (tracefork || tracevforkdone) {
+	if (tracevforkdone) {
 		ksiginfo_t ksi;
 
 		KSI_INIT_EMPTY(&ksi);
@@ -599,6 +613,8 @@ fork1(struct lwp *l1, int flags, int exi
 		ksi.ksi_code = TRAP_CHLD;
 		ksi.ksi_lid = l1->l_lid;
 		kpsignal(p1, &ksi, NULL);
+
+		p1->p_vfpid_done = retval[0];
 	}
 	mutex_exit(proc_lock);
 

Index: src/sys/kern/kern_sig.c
diff -u src/sys/kern/kern_sig.c:1.341 src/sys/kern/kern_sig.c:1.342
--- src/sys/kern/kern_sig.c:1.341	Tue May  1 13:48:38 2018
+++ src/sys/kern/kern_sig.c	Tue May  1 16:37:23 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_sig.c,v 1.341 2018/05/01 13:48:38 kamil Exp $	*/
+/*	$NetBSD: kern_sig.c,v 1.342 2018/05/01 16:37:23 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.341 2018/05/01 13:48:38 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.342 2018/05/01 16:37:23 kamil Exp $");
 
 #include "opt_ptrace.h"
 #include "opt_dtrace.h"
@@ -118,12 +118,12 @@ sigset_t		sigcantmask	__cacheline_aligne
 
 static void	ksiginfo_exechook(struct proc *, void *);
 static void	proc_stop(struct proc *, int);
+static void	proc_stop_done(struct proc *, int);
 static void	proc_stop_callout(void *);
 static int	sigchecktrace(void);
 static int	sigpost(struct lwp *, sig_t, int, int);
 static int	sigput(sigpend_t *, struct proc *, ksiginfo_t *);
 static int	sigunwait(struct proc *, const ksiginfo_t *);
-static void	sigswitch(int, int);
 
 static void	sigacts_poolpage_free(struct pool *, void *);
 static void	*sigacts_poolpage_alloc(struct pool *, int);
@@ -1528,8 +1528,8 @@ proc_stop_done(struct proc *p, int ppmas
 /*
  * Stop the current process and switch away when being stopped or traced.
  */
-static void
-sigswitch(int ppmask, int signo)
+void
+sigswitch(int ppmask, int signo, bool relock)
 {
 	struct lwp *l = curlwp;
 	struct proc *p = l->l_proc;
@@ -1555,7 +1555,7 @@ sigswitch(int ppmask, int signo)
 	 * a new signal, then signal the parent.
 	 */
 	if ((p->p_sflag & PS_STOPPING) != 0) {
-		if (!mutex_tryenter(proc_lock)) {
+		if (relock && !mutex_tryenter(proc_lock)) {
 			mutex_exit(p->p_lock);
 			mutex_enter(proc_lock);
 			mutex_enter(p->p_lock);
@@ -1665,7 +1665,7 @@ issignal(struct lwp *l)
 		 * we awaken, check for a signal from the debugger.
 		 */
 		if (p->p_stat == SSTOP || (p->p_sflag & PS_STOPPING) != 0) {
-			sigswitch(PS_NOCLDSTOP, 0);
+			sigswitch(PS_NOCLDSTOP, 0, true);
 			signo = sigchecktrace();
 		} else
 			signo = 0;
@@ -1718,11 +1718,12 @@ issignal(struct lwp *l)
 
 		/*
 		 * If traced, always stop, and stay stopped until released
-		 * by the debugger.  If the our parent process is waiting
-		 * for us, don't hang as we could deadlock.
+		 * by the debugger.  If the our parent is our debugger waiting
+		 * for us and we vforked, don't hang as we could deadlock.
+		 *
+		 * XXX: support PT_TRACE_ME called after vfork(2)
 		 */
-		if ((p->p_slflag & PSL_TRACED) != 0 &&
-		    (p->p_lflag & PL_PPWAIT) == 0 && signo != SIGKILL) {
+		if ((p->p_slflag & PSL_TRACED) != 0 && signo != SIGKILL) {
 			/*
 			 * Take the signal, but don't remove it from the
 			 * siginfo queue, because the debugger can send
@@ -1735,7 +1736,7 @@ issignal(struct lwp *l)
 			/* Emulation-specific handling of signal trace */
 			if (p->p_emul->e_tracesig == NULL ||
 			    (*p->p_emul->e_tracesig)(p, signo) == 0)
-				sigswitch(0, signo);
+				sigswitch(0, signo, true);
 
 			/* Check for a signal from the debugger. */
 			if ((signo = sigchecktrace()) == 0)
@@ -1789,7 +1790,7 @@ issignal(struct lwp *l)
 				p->p_xsig = signo;
 				p->p_sflag &= ~PS_CONTINUED;
 				signo = 0;
-				sigswitch(PS_NOCLDSTOP, p->p_xsig);
+				sigswitch(PS_NOCLDSTOP, p->p_xsig, true);
 			} else if (prop & SA_IGNORE) {
 				/*
 				 * Except for SIGCONT, shouldn't get here.
@@ -2298,7 +2299,7 @@ proc_stoptrace(int trapno)
 	p->p_xsig = signo;
 	p->p_sigctx.ps_lwp = ksi.ksi_lid;
 	p->p_sigctx.ps_info = ksi.ksi_info;
-	sigswitch(0, signo);
+	sigswitch(0, signo, true);
 	mutex_exit(p->p_lock);
 
 	if (ktrpoint(KTR_PSIG)) {

Index: src/sys/kern/sys_ptrace_common.c
diff -u src/sys/kern/sys_ptrace_common.c:1.39 src/sys/kern/sys_ptrace_common.c:1.40
--- src/sys/kern/sys_ptrace_common.c:1.39	Tue May  1 14:09:53 2018
+++ src/sys/kern/sys_ptrace_common.c	Tue May  1 16:37:23 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_ptrace_common.c,v 1.39 2018/05/01 14:09:53 kamil Exp $	*/
+/*	$NetBSD: sys_ptrace_common.c,v 1.40 2018/05/01 16:37:23 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -118,7 +118,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.39 2018/05/01 14:09:53 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.40 2018/05/01 16:37:23 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ptrace.h"
@@ -634,23 +634,22 @@ ptrace_set_event_mask(struct proc *t, vo
 		SET(t->p_slflag, PSL_TRACEFORK);
 	else
 		CLR(t->p_slflag, PSL_TRACEFORK);
-#if notyet
+
 	if (pe.pe_set_event & PTRACE_VFORK)
 		SET(t->p_slflag, PSL_TRACEVFORK);
 	else
 		CLR(t->p_slflag, PSL_TRACEVFORK);
-#else
-	if (pe.pe_set_event & PTRACE_VFORK)
-		return ENOTSUP;
-#endif
+
 	if (pe.pe_set_event & PTRACE_VFORK_DONE)
 		SET(t->p_slflag, PSL_TRACEVFORK_DONE);
 	else
 		CLR(t->p_slflag, PSL_TRACEVFORK_DONE);
+
 	if (pe.pe_set_event & PTRACE_LWP_CREATE)
 		SET(t->p_slflag, PSL_TRACELWP_CREATE);
 	else
 		CLR(t->p_slflag, PSL_TRACELWP_CREATE);
+
 	if (pe.pe_set_event & PTRACE_LWP_EXIT)
 		SET(t->p_slflag, PSL_TRACELWP_EXIT);
 	else

Index: src/sys/sys/signalvar.h
diff -u src/sys/sys/signalvar.h:1.89 src/sys/sys/signalvar.h:1.90
--- src/sys/sys/signalvar.h:1.89	Thu Apr 19 21:19:07 2018
+++ src/sys/sys/signalvar.h	Tue May  1 16:37:23 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: signalvar.h,v 1.89 2018/04/19 21:19:07 christos Exp $	*/
+/*	$NetBSD: signalvar.h,v 1.90 2018/05/01 16:37:23 kamil Exp $	*/
 
 /*
  * Copyright (c) 1991, 1993
@@ -136,6 +136,8 @@ void	killproc(struct proc *, const char 
 void	setsigvec(struct proc *, int, struct sigaction *);
 int	killpg1(struct lwp *, struct ksiginfo *, int, int);
 void	proc_unstop(struct proc *p);
+void	sigswitch(int, int, bool);
+
 
 int	sigaction1(struct lwp *, int, const struct sigaction *,
 	    struct sigaction *, const void *, int);

Index: src/tests/lib/libc/sys/t_ptrace_wait.c
diff -u src/tests/lib/libc/sys/t_ptrace_wait.c:1.37 src/tests/lib/libc/sys/t_ptrace_wait.c:1.38
--- src/tests/lib/libc/sys/t_ptrace_wait.c:1.37	Sun Apr 29 13:56:00 2018
+++ src/tests/lib/libc/sys/t_ptrace_wait.c	Tue May  1 16:37:23 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_ptrace_wait.c,v 1.37 2018/04/29 13:56:00 kamil Exp $	*/
+/*	$NetBSD: t_ptrace_wait.c,v 1.38 2018/05/01 16:37:23 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: t_ptrace_wait.c,v 1.37 2018/04/29 13:56:00 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.38 2018/05/01 16:37:23 kamil Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -1189,8 +1189,6 @@ ATF_TC_BODY(eventmask3, tc)
 	ptrace_event_t set_event, get_event;
 	const int len = sizeof(ptrace_event_t);
 
-	atf_tc_expect_fail("PR kern/51630");
-
 	DPRINTF("Before forking process PID=%d\n", getpid());
 	SYSCALL_REQUIRE((child = fork()) != -1);
 	if (child == 0) {
@@ -1211,7 +1209,7 @@ ATF_TC_BODY(eventmask3, tc)
 	validate_status_stopped(status, sigval);
 
 	set_event.pe_set_event = PTRACE_VFORK;
-	SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1 || errno == ENOTSUP);
+	SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
 	SYSCALL_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
 	ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
 
@@ -1409,10 +1407,6 @@ fork_body(pid_t (*fn)(void), bool trackf
 	ptrace_event_t event;
 	const int elen = sizeof(event);
 
-	if (trackvfork) {
-		atf_tc_expect_fail("PR kern/51630");
-	}
-
 	DPRINTF("Before forking process PID=%d\n", getpid());
 	SYSCALL_REQUIRE((child = fork()) != -1);
 	if (child == 0) {
@@ -5722,7 +5716,7 @@ ATF_TC_BODY(signal6, tc)
 	ptrace_event_t event;
 	const int elen = sizeof(event);
 
-	atf_tc_expect_timeout("PR kern/51918");
+	atf_tc_expect_fail("PR kern/51918");
 
 	DPRINTF("Before forking process PID=%d\n", getpid());
 	SYSCALL_REQUIRE((child = fork()) != -1);
@@ -5853,7 +5847,7 @@ ATF_TC_BODY(signal7, tc)
 	ptrace_event_t event;
 	const int elen = sizeof(event);
 
-	atf_tc_expect_fail("PR kern/51918 PR kern/51630");
+	atf_tc_expect_fail("PR kern/51918");
 
 	DPRINTF("Before forking process PID=%d\n", getpid());
 	SYSCALL_REQUIRE((child = fork()) != -1);
@@ -6676,6 +6670,8 @@ ATF_TC_BODY(syscall1, tc)
 	DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
 	SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
 
+	DPRINTF("Before checking siginfo_t and lwpid\n");
+	ATF_REQUIRE_EQ(info.psi_lwpid, 1);
 	ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
 	ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_SCE);
 
@@ -6691,7 +6687,8 @@ ATF_TC_BODY(syscall1, tc)
 	DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
 	SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
 
-	DPRINTF("Before checking siginfo_t\n");
+	DPRINTF("Before checking siginfo_t and lwpid\n");
+	ATF_REQUIRE_EQ(info.psi_lwpid, 1);
 	ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
 	ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_SCX);
 

Reply via email to