Module Name:    src
Committed By:   kamil
Date:           Wed May 16 00:42:16 UTC 2018

Modified Files:
        src/sys/kern: kern_sig.c
        src/tests/lib/libc/sys: t_ptrace_wait.c

Log Message:
Correct handling of: vfork(2) + PT_TRACE_ME + raise(2)

Follow the FreeBSD approach of not routing signals to the parent that is
a became tracer after calling PT_TRACE_ME by the vfork(2)ed child (before
exec(3)/exit(3)).

Now if a child calls raise(3), the signal is processed directly to this
child.

Add new ATF ptrace(2) tests:
 - traceme_vfork_raise1 (SIGKILL)
 - traceme_vfork_raise2 (SIGSTOP) // temporarily disabled
 - traceme_vfork_raise3 (SIGABRT)
 - traceme_vfork_raise4 (SIGHUP)
 - traceme_vfork_raise5 (SIGCONT)

The FreeBSD implementation introduces P_PPTRACE for this special case.
Right know keep opencoding check of this case in the kernel. It might be
refactored in future.

The Linux kernel does not follow this approach and causes dead locking of
the processes (parent and child).

Defer handling SIGSTOP into future.

This is an intermediate step towards correct handling of fork(2) and
vfork(2) in the context of ptrace(2).

All new tests pass.
There are no regressions in existing ATF ptrace(2) tests.

Sponsored by <The NetBSD Foundation>


To generate a diff of this commit:
cvs rdiff -u -r1.343 -r1.344 src/sys/kern/kern_sig.c
cvs rdiff -u -r1.39 -r1.40 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/sys/kern/kern_sig.c
diff -u src/sys/kern/kern_sig.c:1.343 src/sys/kern/kern_sig.c:1.344
--- src/sys/kern/kern_sig.c:1.343	Sun May  6 13:40:51 2018
+++ src/sys/kern/kern_sig.c	Wed May 16 00:42:15 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_sig.c,v 1.343 2018/05/06 13:40:51 kamil Exp $	*/
+/*	$NetBSD: kern_sig.c,v 1.344 2018/05/16 00:42:15 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.343 2018/05/06 13:40:51 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.344 2018/05/16 00:42:15 kamil Exp $");
 
 #include "opt_ptrace.h"
 #include "opt_dtrace.h"
@@ -1720,10 +1720,10 @@ issignal(struct lwp *l)
 		 * If traced, always stop, and stay stopped until released
 		 * 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 && signo != SIGKILL) {
+		if (ISSET(p->p_slflag, PSL_TRACED) && signo != SIGKILL &&
+		    !(ISSET(p->p_lflag, PL_PPWAIT) &&
+		     (p->p_pptr == p->p_opptr))) {
 			/*
 			 * Take the signal, but don't remove it from the
 			 * siginfo queue, because the debugger can send

Index: src/tests/lib/libc/sys/t_ptrace_wait.c
diff -u src/tests/lib/libc/sys/t_ptrace_wait.c:1.39 src/tests/lib/libc/sys/t_ptrace_wait.c:1.40
--- src/tests/lib/libc/sys/t_ptrace_wait.c:1.39	Sun May 13 23:14:47 2018
+++ src/tests/lib/libc/sys/t_ptrace_wait.c	Wed May 16 00:42:15 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_ptrace_wait.c,v 1.39 2018/05/13 23:14:47 kamil Exp $	*/
+/*	$NetBSD: t_ptrace_wait.c,v 1.40 2018/05/16 00:42:15 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.39 2018/05/13 23:14:47 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.40 2018/05/16 00:42:15 kamil Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -379,6 +379,83 @@ ATF_TC_BODY(traceme_pid1_parent, tc)
 
 /// ----------------------------------------------------------------------------
 
+static void
+traceme_vfork_raise(int sigval)
+{
+	const int exitval = 5;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+	int expect_core = (sigval == SIGABRT) ? 1 : 0;
+#endif
+
+	DPRINTF("Before forking process PID=%d\n", getpid());
+	SYSCALL_REQUIRE((child = vfork()) != -1);
+	if (child == 0) {
+		DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+		DPRINTF("Before raising %s from child\n", strsignal(sigval));
+		FORKEE_ASSERT(raise(sigval) == 0);
+
+		switch (sigval) {
+		case SIGKILL:
+		case SIGABRT:
+		case SIGHUP:
+			/* NOTREACHED */
+			FORKEE_ASSERTX(0 && "This shall not be reached");
+		default:
+			DPRINTF("Before exiting of the child process\n");
+			_exit(exitval);
+		}
+	}
+	DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+	DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	switch (sigval) {
+	case SIGKILL:
+	case SIGABRT:
+	case SIGHUP:
+		validate_status_signaled(status, sigval, expect_core);
+		break;
+	case SIGSTOP:
+	case SIGCONT:
+		validate_status_exited(status, exitval);
+		break;
+	default:
+		/* NOTREACHED */
+		ATF_REQUIRE(0 && "NOT IMPLEMENTED");
+		break;
+	}
+
+	DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#define TRACEME_VFORK_RAISE(test, sig)						\
+ATF_TC(test);									\
+ATF_TC_HEAD(test, tc)								\
+{										\
+	atf_tc_set_md_var(tc, "descr",						\
+	    "Verify " #sig " followed by _exit(2) in a vfork(2)ed child");	\
+}										\
+										\
+ATF_TC_BODY(test, tc)								\
+{										\
+										\
+	traceme_vfork_raise(sig);						\
+}
+
+TRACEME_VFORK_RAISE(traceme_vfork_raise1, SIGKILL) /* non-maskable */
+// TRACEME_VFORK_RAISE(traceme_vfork_raise2, SIGSTOP) /* non-maskable */ // TODO
+TRACEME_VFORK_RAISE(traceme_vfork_raise3, SIGABRT) /* regular abort trap */
+TRACEME_VFORK_RAISE(traceme_vfork_raise4, SIGHUP)  /* hangup */
+TRACEME_VFORK_RAISE(traceme_vfork_raise5, SIGCONT) /* continued? */
+
+/// ----------------------------------------------------------------------------
+
 #if defined(TWAIT_HAVE_PID)
 ATF_TC(attach1);
 ATF_TC_HEAD(attach1, tc)
@@ -6840,6 +6917,12 @@ ATF_TP_ADD_TCS(tp)
 
 	ATF_TP_ADD_TC(tp, traceme_pid1_parent);
 
+	ATF_TP_ADD_TC(tp, traceme_vfork_raise1);
+//	ATF_TP_ADD_TC(tp, traceme_vfork_raise2); // not yet
+	ATF_TP_ADD_TC(tp, traceme_vfork_raise3);
+	ATF_TP_ADD_TC(tp, traceme_vfork_raise4);
+	ATF_TP_ADD_TC(tp, traceme_vfork_raise5);
+
 	ATF_TP_ADD_TC_HAVE_PID(tp, attach1);
 	ATF_TP_ADD_TC_HAVE_PID(tp, attach2);
 	ATF_TP_ADD_TC(tp, attach3);

Reply via email to