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:
parent
2c2a96663a
commit
48b46ced17
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, <, 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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue