Introduce new ptrace(2) operations: PT_SET_SIGPASS and PT_GET_SIGPASS

They deliver the logic of bypassing selected signals directly to the
debuggee, without informing the debugger.

This can be used to implement the QPassSignals GDB/LLDB protocol.

This call can be useful to avoid signal races in ATF ptrace tests.
This commit is contained in:
kamil 2020-05-14 13:32:15 +00:00
parent 2c2a96663a
commit 48b46ced17
5 changed files with 111 additions and 19 deletions

View File

@ -1,7 +1,7 @@
.\" $NetBSD: ptrace.2,v 1.83 2020/01/04 04:40:17 kamil Exp $
.\" $NetBSD: ptrace.2,v 1.84 2020/05/14 13:32:15 kamil Exp $
.\"
.\" This file is in the public domain.
.Dd January 4, 2019
.Dd May 14, 2020
.Dt PTRACE 2
.Os
.Sh NAME
@ -623,6 +623,31 @@ The
.Fa data
argument should be set to
.Li sizeof(struct ptrace_siginfo) .
.It Dv PT_SET_SIGPASS
This request can be used to specify mask of signals that should be passed
directly to the debuggee, without reporting to the tracer.
A pointer to sigset_t is passed in
.Fa addr .
The
.Fa data
argument should be set to
.Li sizeof(sigset_t) .
.Pp
It is not permitted to mask SIGSTOP and SIGKILL.
All debugger related signals
(SIGTRAP, SIGILL, SIGSEGV, SIGBUS, SIGFPE)
are reported to the tracer without interruption,
unless they were emitted by a non-crash source.
.It Dv PT_GET_SIGPASS
This request can be used to determine mask of signals passed directly to the debuggee.
A pointer to sigset_t is passed in
.Fa addr .
The
.Fa data
argument should be set to
.Li sizeof(sigset_t) .
.Pp
Upon debugger attach the sigpass mask shall be empty.
.It Dv PT_RESUME
Allow execution of a specified thread,
change its state from suspended to continued.

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_sig.c,v 1.388 2020/05/07 20:02:34 kamil Exp $ */
/* $NetBSD: kern_sig.c,v 1.389 2020/05/14 13:32:15 kamil Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008, 2019 The NetBSD Foundation, Inc.
@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.388 2020/05/07 20:02:34 kamil Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.389 2020/05/14 13:32:15 kamil Exp $");
#include "opt_ptrace.h"
#include "opt_dtrace.h"
@ -1302,6 +1302,7 @@ kpsignal2(struct proc *p, ksiginfo_t *ksi)
lwpid_t lid;
sig_t action;
bool toall;
bool traced;
int error = 0;
KASSERT(!cpu_intr_p());
@ -1329,11 +1330,13 @@ kpsignal2(struct proc *p, ksiginfo_t *ksi)
prop = sigprop[signo];
toall = ((prop & SA_TOALL) != 0);
lid = toall ? 0 : ksi->ksi_lid;
traced = ISSET(p->p_slflag, PSL_TRACED) &&
!sigismember(&p->p_sigctx.ps_sigpass, signo);
/*
* If proc is traced, always give parent a chance.
*/
if (p->p_slflag & PSL_TRACED) {
if (traced) {
action = SIG_DFL;
if (lid == 0) {
@ -1428,7 +1431,7 @@ kpsignal2(struct proc *p, ksiginfo_t *ksi)
* or for an SA process.
*/
if (p->p_stat == SACTIVE && (p->p_sflag & PS_STOPPING) == 0) {
if ((p->p_slflag & PSL_TRACED) != 0)
if (traced)
goto deliver;
/*
@ -1444,7 +1447,7 @@ kpsignal2(struct proc *p, ksiginfo_t *ksi)
* - If traced, then no action is needed, unless killing.
* - Run the process only if sending SIGCONT or SIGKILL.
*/
if ((p->p_slflag & PSL_TRACED) != 0 && signo != SIGKILL) {
if (traced && signo != SIGKILL) {
goto out;
}
if ((prop & SA_CONT) != 0 || signo == SIGKILL) {
@ -1456,7 +1459,7 @@ kpsignal2(struct proc *p, ksiginfo_t *ksi)
p->p_pptr->p_nstopchild--;
p->p_stat = SACTIVE;
p->p_sflag &= ~PS_STOPPING;
if (p->p_slflag & PSL_TRACED) {
if (traced) {
KASSERT(signo == SIGKILL);
goto deliver;
}
@ -1487,7 +1490,7 @@ kpsignal2(struct proc *p, ksiginfo_t *ksi)
/*
* Make signal pending.
*/
KASSERT((p->p_slflag & PSL_TRACED) == 0);
KASSERT(!traced);
if ((error = sigput(&p->p_sigpend, p, kp)) != 0)
goto out;
deliver:
@ -1844,6 +1847,7 @@ issignal(struct lwp *l)
int siglwp, signo, prop;
sigpend_t *sp;
sigset_t ss;
bool traced;
p = l->l_proc;
sp = NULL;
@ -1910,6 +1914,9 @@ issignal(struct lwp *l)
}
}
traced = ISSET(p->p_slflag, PSL_TRACED) &&
!sigismember(&p->p_sigctx.ps_sigpass, signo);
if (sp) {
/* Overwrite process' signal context to correspond
* to the currently reported LWP. This is necessary
@ -1937,7 +1944,7 @@ issignal(struct lwp *l)
* we are being traced.
*/
if (sigismember(&p->p_sigctx.ps_sigignore, signo) &&
(p->p_slflag & PSL_TRACED) == 0) {
!traced) {
/* Discard the signal. */
continue;
}
@ -1947,7 +1954,7 @@ issignal(struct lwp *l)
* by the debugger. If the our parent is our debugger waiting
* for us and we vforked, don't hang as we could deadlock.
*/
if (ISSET(p->p_slflag, PSL_TRACED) && signo != SIGKILL &&
if (traced && signo != SIGKILL &&
!(ISSET(p->p_lflag, PL_PPWAIT) &&
(p->p_pptr == p->p_opptr))) {
/*
@ -2004,7 +2011,7 @@ issignal(struct lwp *l)
* XXX Don't hold proc_lock for p_lflag,
* but it's not a big deal.
*/
if ((ISSET(p->p_slflag, PSL_TRACED) &&
if ((traced &&
!(ISSET(p->p_lflag, PL_PPWAIT) &&
(p->p_pptr == p->p_opptr))) ||
((p->p_lflag & PL_ORPHANPG) != 0 &&
@ -2035,8 +2042,7 @@ issignal(struct lwp *l)
* to take action on an ignored signal other
* than SIGCONT, unless process is traced.
*/
if ((prop & SA_CONT) == 0 &&
(p->p_slflag & PSL_TRACED) == 0)
if ((prop & SA_CONT) == 0 && !traced)
printf_nolog("issignal\n");
#endif
continue;

View File

@ -1,4 +1,4 @@
/* $NetBSD: sys_ptrace_common.c,v 1.79 2020/05/08 10:35:51 kamil Exp $ */
/* $NetBSD: sys_ptrace_common.c,v 1.80 2020/05/14 13:32:15 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.79 2020/05/08 10:35:51 kamil Exp $");
__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.80 2020/05/14 13:32:15 kamil Exp $");
#ifdef _KERNEL_OPT
#include "opt_ptrace.h"
@ -290,6 +290,8 @@ ptrace_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
case PT_STOP:
case PT_LWPSTATUS:
case PT_LWPNEXT:
case PT_SET_SIGPASS:
case PT_GET_SIGPASS:
result = KAUTH_RESULT_ALLOW;
break;
@ -500,6 +502,8 @@ ptrace_allowed(struct lwp *l, int req, struct proc *t, struct proc *p,
case PT_STOP:
case PT_LWPSTATUS:
case PT_LWPNEXT:
case PT_SET_SIGPASS:
case PT_GET_SIGPASS:
/*
* You can't do what you want to the process if:
* (1) It's not being traced at all,
@ -621,6 +625,47 @@ ptrace_set_siginfo(struct proc *t, struct lwp **lt, struct ptrace_methods *ptm,
return 0;
}
static int
ptrace_get_sigpass(struct proc *t, void *addr, size_t data)
{
sigset_t set;
if (data > sizeof(set) || data <= 0) {
DPRINTF(("%s: invalid data: %zu < %zu <= 0\n",
__func__, sizeof(set), data));
return EINVAL;
}
set = t->p_sigctx.ps_sigpass;
return copyout(&set, addr, data);
}
static int
ptrace_set_sigpass(struct proc *t, void *addr, size_t data)
{
sigset_t set;
int error;
if (data > sizeof(set) || data <= 0) {
DPRINTF(("%s: invalid data: %zu < %zu <= 0\n",
__func__, sizeof(set), data));
return EINVAL;
}
memset(&set, 0, sizeof(set));
if ((error = copyin(addr, &set, data)))
return error;
/* We catch SIGSTOP and cannot intercept SIGKILL. */
sigminusset(&sigcantmask, &set);
t->p_sigctx.ps_sigpass = set;
return 0;
}
static int
ptrace_get_event_mask(struct proc *t, void *addr, size_t data)
{
@ -1395,6 +1440,9 @@ do_ptrace(struct ptrace_methods *ptm, struct lwp *l, int req, pid_t pid,
CLR(t->p_slflag,
PSL_TRACED|PSL_TRACEDCHILD|PSL_SYSCALL);
/* clear sigpass mask */
sigemptyset(&t->p_sigctx.ps_sigpass);
/* give process back to original parent or init */
if (t->p_opptr != t->p_pptr) {
struct proc *pp = t->p_opptr;
@ -1499,6 +1547,14 @@ do_ptrace(struct ptrace_methods *ptm, struct lwp *l, int req, pid_t pid,
error = ptrace_lwpstatus(t, ptm, &lt, addr, data, true);
break;
case PT_SET_SIGPASS:
error = ptrace_set_sigpass(t, addr, data);
break;
case PT_GET_SIGPASS:
error = ptrace_get_sigpass(t, addr, data);
break;
#ifdef PT_REGISTERS
case_PT_SETREGS
case_PT_GETREGS

View File

@ -1,4 +1,4 @@
/* $NetBSD: ptrace.h,v 1.69 2019/12/26 08:52:38 kamil Exp $ */
/* $NetBSD: ptrace.h,v 1.70 2020/05/14 13:32:15 kamil Exp $ */
/*-
* Copyright (c) 1984, 1993
@ -63,6 +63,8 @@
#define PT_STOP 23 /* stop the child process */
#define PT_LWPSTATUS 24 /* get info about the LWP */
#define PT_LWPNEXT 25 /* get info about next LWP */
#define PT_SET_SIGPASS 26 /* set signals to pass to debuggee */
#define PT_GET_SIGPASS 27 /* get signals to pass to debuggee */
#define PT_FIRSTMACH 32 /* for machine-specific requests */
#include <machine/ptrace.h> /* machine-specific requests, if any */
@ -93,7 +95,9 @@
/* 22 */ "PT_SUSPEND", \
/* 23 */ "PT_STOP", \
/* 24 */ "PT_LWPSTATUS", \
/* 25 */ "PT_LWPNEXT"
/* 25 */ "PT_LWPNEXT", \
/* 26 */ "PT_SET_SIGPASS", \
/* 27 */ "PT_GET_SIGPASS"
/* PT_{G,S}EVENT_MASK */
typedef struct ptrace_event {

View File

@ -1,4 +1,4 @@
/* $NetBSD: signalvar.h,v 1.101 2020/04/05 20:53:17 christos Exp $ */
/* $NetBSD: signalvar.h,v 1.102 2020/05/14 13:32:15 kamil Exp $ */
/*
* Copyright (c) 1991, 1993
@ -85,6 +85,7 @@ struct sigctx {
void *ps_sigcode; /* address of signal trampoline */
sigset_t ps_sigignore; /* Signals being ignored. */
sigset_t ps_sigcatch; /* Signals being caught by user. */
sigset_t ps_sigpass; /* Signals evading the debugger. */
};
/* additional signal action values, used only temporarily/internally */