diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -2,7 +2,7 @@ .\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $ .\" .\" This file is in the public domain. -.Dd May 4, 2021 +.Dd May 20, 2021 .Dt PTRACE 2 .Os .Sh NAME @@ -100,6 +100,18 @@ All other additional signal stops use .Dv SIGTRAP . .Pp +Normally, tracing process should detach from all traced processes +before exiting using +.Dv PT_DETACH +request. +If tracing process exits without detaching, for instance due to abnormal +termination, the destiny of the traced processes is determined by the +.Dv kern.kill_on_debugger_exit +sysctl value. +If it is set to the default value 1, such traced processes are terminated. +If set to zero, kernel implicitly detaches tracing process, traced processes +are continued if stopped, and then continue the execution without tracing. +.Pp Each traced process has a tracing event mask. An event in the traced process only reports a signal stop if the corresponding flag is set in the tracing event mask. diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -504,8 +504,9 @@ } } else { /* - * Traced processes are killed since their existence - * means someone is screwing up. + * Traced processes are killed by default + * since their existence means someone is + * screwing up. */ t = proc_realparent(q); if (t == p) { @@ -522,14 +523,20 @@ * orphan link for q now while q is locked. */ proc_clear_orphan(q); - q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE); + q->p_flag &= ~P_TRACED; q->p_flag2 &= ~P2_PTRACE_FSTP; q->p_ptevents = 0; FOREACH_THREAD_IN_PROC(q, tdt) { tdt->td_dbgflags &= ~(TDB_SUSPEND | TDB_XSIG | TDB_FSTP); } - kern_psignal(q, SIGKILL); + if (kern_kill_on_dbg_exit) { + q->p_flag &= ~P_STOPPED_TRACE; + kern_psignal(q, SIGKILL); + } else if ((q->p_flag & (P_STOPPED_TRACE | + P_STOPPED_SIG)) != 0) { + ptrace_unsuspend(q); + } } PROC_UNLOCK(q); if (ksi != NULL) diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -163,6 +163,11 @@ "Fetch sigfastblock word on each syscall entry for proper " "blocking semantic"); +int kern_kill_on_dbg_exit = 1; +SYSCTL_INT(_kern, OID_AUTO, kill_on_debugger_exit, CTLFLAG_RWTUN, + &kern_kill_on_dbg_exit, 0, + "Kill ptraced processes when debugger exits"); + SYSINIT(signal, SI_SUB_P1003_1B, SI_ORDER_FIRST+3, sigqueue_start, NULL); /* diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -610,6 +610,19 @@ p->p_ptevents = PTRACE_DEFAULT; } +void +ptrace_unsuspend(struct proc *p) +{ + PROC_LOCK_ASSERT(p, MA_OWNED); + + PROC_SLOCK(p); + p->p_flag &= ~(P_STOPPED_TRACE | P_STOPPED_SIG | P_WAITED); + thread_unsuspend(p); + PROC_SUNLOCK(p); + itimer_proc_continue(p); + kqtimer_proc_continue(p); +} + static int proc_can_ptrace(struct thread *td, struct proc *p) { @@ -1164,12 +1177,7 @@ * suspended, use PT_SUSPEND to suspend it before * continuing the process. */ - PROC_SLOCK(p); - p->p_flag &= ~(P_STOPPED_TRACE | P_STOPPED_SIG | P_WAITED); - thread_unsuspend(p); - PROC_SUNLOCK(p); - itimer_proc_continue(p); - kqtimer_proc_continue(p); + ptrace_unsuspend(p); break; case PT_WRITE_I: diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -240,6 +240,9 @@ int proc_read_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32); int proc_write_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32); #endif + +void ptrace_unsuspend(struct proc *p); + #else /* !_KERNEL */ #include diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h --- a/sys/sys/signalvar.h +++ b/sys/sys/signalvar.h @@ -274,6 +274,7 @@ #ifdef _KERNEL extern sigset_t fastblock_mask; extern bool sigfastblock_fetch_always; +extern int kern_kill_on_dbg_exit; /* Return nonzero if process p has an unmasked pending signal. */ #define SIGPENDING(td) \