diff options
| author | kamil <kamil@NetBSD.org> | 2018-05-01 16:37:23 +0000 |
|---|---|---|
| committer | kamil <kamil@NetBSD.org> | 2018-05-01 16:37:23 +0000 |
| commit | 9d02027080155d5c5f8a4aa515c658328fa6eeb2 (patch) | |
| tree | 4319129cf2d201608e4282cde4e6e2c08d1a1c98 /sys/kern/kern_fork.c | |
| parent | 5366ef43b53d63d81ecc71786daabcae5f6e0432 (diff) | |
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 (limited to 'sys/kern/kern_fork.c')
| -rw-r--r-- | sys/kern/kern_fork.c | 44 |
1 files changed, 30 insertions, 14 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 02b63231653..22cf730fa4a 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -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 exitsig, void *stack, size_t stacksize, 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 exitsig, void *stack, size_t stacksize, */ 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 exitsig, void *stack, size_t stacksize, 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 exitsig, void *stack, size_t stacksize, 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); |
