Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src Implement PTRACE_VFORK
details: https://anonhg.NetBSD.org/src/rev/efc95b05eb7d
branches: trunk
changeset: 318620:efc95b05eb7d
user: kamil <kamil%NetBSD.org@localhost>
date: Tue May 01 16:37:23 2018 +0000
description:
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>
diffstat:
lib/libc/sys/ptrace.2 | 8 +-----
sys/kern/kern_fork.c | 44 +++++++++++++++++++++++++------------
sys/kern/kern_sig.c | 29 ++++++++++++------------
sys/kern/sys_ptrace_common.c | 13 +++++------
sys/sys/signalvar.h | 4 ++-
tests/lib/libc/sys/t_ptrace_wait.c | 21 +++++++----------
6 files changed, 65 insertions(+), 54 deletions(-)
diffs (truncated from 380 to 300 lines):
diff -r c5ce9af4eb00 -r efc95b05eb7d lib/libc/sys/ptrace.2
--- a/lib/libc/sys/ptrace.2 Tue May 01 16:18:13 2018 +0000
+++ b/lib/libc/sys/ptrace.2 Tue May 01 16:37:23 2018 +0000
@@ -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 @@
.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
diff -r c5ce9af4eb00 -r efc95b05eb7d sys/kern/kern_fork.c
--- a/sys/kern/kern_fork.c Tue May 01 16:18:13 2018 +0000
+++ b/sys/kern/kern_fork.c Tue May 01 16:37:23 2018 +0000
@@ -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 @@
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 @@
*/
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 @@
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 @@
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);
diff -r c5ce9af4eb00 -r efc95b05eb7d sys/kern/kern_sig.c
--- a/sys/kern/kern_sig.c Tue May 01 16:18:13 2018 +0000
+++ b/sys/kern/kern_sig.c Tue May 01 16:37:23 2018 +0000
@@ -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 @@
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 @@
/*
* 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 @@
* 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 @@
* 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 @@
/*
* 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 @@
/* 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 @@
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 @@
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)) {
diff -r c5ce9af4eb00 -r efc95b05eb7d sys/kern/sys_ptrace_common.c
--- a/sys/kern/sys_ptrace_common.c Tue May 01 16:18:13 2018 +0000
+++ b/sys/kern/sys_ptrace_common.c Tue May 01 16:37:23 2018 +0000
@@ -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 @@
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
diff -r c5ce9af4eb00 -r efc95b05eb7d sys/sys/signalvar.h
--- a/sys/sys/signalvar.h Tue May 01 16:18:13 2018 +0000
+++ b/sys/sys/signalvar.h Tue May 01 16:37:23 2018 +0000
@@ -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 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);
diff -r c5ce9af4eb00 -r efc95b05eb7d tests/lib/libc/sys/t_ptrace_wait.c
--- a/tests/lib/libc/sys/t_ptrace_wait.c Tue May 01 16:18:13 2018 +0000
+++ b/tests/lib/libc/sys/t_ptrace_wait.c Tue May 01 16:37:23 2018 +0000
@@ -1,4 +1,4 @@
Home |
Main Index |
Thread Index |
Old Index