Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Correct handling of: vfork(2) + PT_TRACE_ME + raise(2)



details:   https://anonhg.NetBSD.org/src/rev/31a693fe515b
branches:  trunk
changeset: 322752:31a693fe515b
user:      kamil <kamil%NetBSD.org@localhost>
date:      Wed May 16 00:42:15 2018 +0000

description:
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>

diffstat:

 sys/kern/kern_sig.c                |  10 ++--
 tests/lib/libc/sys/t_ptrace_wait.c |  87 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 7 deletions(-)

diffs (147 lines):

diff -r 10961a797480 -r 31a693fe515b sys/kern/kern_sig.c
--- a/sys/kern/kern_sig.c       Wed May 16 00:12:57 2018 +0000
+++ b/sys/kern/kern_sig.c       Wed May 16 00:42:15 2018 +0000
@@ -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 @@
                 * 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
diff -r 10961a797480 -r 31a693fe515b tests/lib/libc/sys/t_ptrace_wait.c
--- a/tests/lib/libc/sys/t_ptrace_wait.c        Wed May 16 00:12:57 2018 +0000
+++ b/tests/lib/libc/sys/t_ptrace_wait.c        Wed May 16 00:42:15 2018 +0000
@@ -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 @@
 
 /// ----------------------------------------------------------------------------
 
+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_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);



Home | Main Index | Thread Index | Old Index