Introduce new interface in ptrace(2) - PT_GET_SIGMASK and PT_SET_SIGMASK

Add new interface to add ability to get/set signal mask of a tracee.
It has been inspired by Linux PTRACE_GETSIGMASK and PTRACE_SETSIGMASK, but
adapted for NetBSD API.

This interface is used for checkpointing software to set/restore context
of a process including signal mask like criu or just to track this property
in reverse-execution software like Record and Replay Framework (rr).


Add new ATF tests for this interface
====================================
getsigmask1:
    Verify that plain PT_SET_SIGMASK can be called

getsigmask2:
    Verify that PT_SET_SIGMASK reports correct mask from tracee

setsigmask1:
    Verify that plain PT_SET_SIGMASK can be called with empty mask

setsigmask2:
    Verify that sigmask is preserved between PT_GET_SIGMASK and
    PT_SET_SIGMASK

setsigmask3:
    Verify that sigmask is preserved between PT_GET_SIGMASK, process
    resumed and PT_SET_SIGMASK

setsigmask4:
    Verify that new sigmask is visible in tracee


Kernel ABI bump delayed as there are more interfaces to come in ptrace(2).

Sponsored by <The NetBSD Foundation>
This commit is contained in:
kamil 2017-02-12 06:09:52 +00:00
parent ac46406cfc
commit 61aff29627
4 changed files with 442 additions and 8 deletions

View File

@ -1,7 +1,7 @@
.\" $NetBSD: ptrace.2,v 1.58 2017/01/27 12:52:39 wiz Exp $
.\" $NetBSD: ptrace.2,v 1.59 2017/02/12 06:09:53 kamil Exp $
.\"
.\" This file is in the public domain.
.Dd January 25, 2016
.Dd February 12, 2016
.Dt PTRACE 2
.Os
.Sh NAME
@ -514,6 +514,31 @@ The
.Fa data
argument should be set to
.Li sizeof(struct ptrace_siginfo) .
.It Dv PT_SET_SIGMASK
This request loads the traced process' signal mask from
.Dq Li "sigset_t"
(defined in
.In sys/sigtypes.h )
pointed to by
.Fa addr .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be written.
If zero is supplied, the first thread of the process is written.
.It Dv PT_GET_SIGMASK
This request is the converse of
.Dv PT_SET_SIGMASK ;
it reads the traced process' signal mask into
.Dq Li "sigset_t"
(defined in
.In sys/sigtypes.h )
pointed to by
.Fa addr .
The
.Fa data
argument contains the LWP ID of the thread whose mask is to be read.
If zero is supplied, the first thread of the process is read.
.El
.Pp
Additionally, the following requests exist but are

View File

@ -1,4 +1,4 @@
/* $NetBSD: sys_ptrace_common.c,v 1.13 2017/02/11 19:32:41 kamil Exp $ */
/* $NetBSD: sys_ptrace_common.c,v 1.14 2017/02/12 06:09:52 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.13 2017/02/11 19:32:41 kamil Exp $");
__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.14 2017/02/12 06:09:52 kamil Exp $");
#ifdef _KERNEL_OPT
#include "opt_ptrace.h"
@ -212,6 +212,8 @@ ptrace_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
case PT_GET_PROCESS_STATE:
case PT_SET_SIGINFO:
case PT_GET_SIGINFO:
case PT_SET_SIGMASK:
case PT_GET_SIGMASK:
#ifdef __HAVE_PTRACE_MACHDEP
PTRACE_MACHDEP_REQUEST_CASES
#endif
@ -406,6 +408,8 @@ do_ptrace(struct ptrace_methods *ptm, struct lwp *l, int req, pid_t pid,
case PT_IO:
case PT_SET_SIGINFO:
case PT_GET_SIGINFO:
case PT_SET_SIGMASK:
case PT_GET_SIGMASK:
#ifdef PT_GETREGS
case PT_GETREGS:
#endif
@ -1039,6 +1043,35 @@ do_ptrace(struct ptrace_methods *ptm, struct lwp *l, int req, pid_t pid,
break;
case PT_SET_SIGMASK:
write = 1;
case PT_GET_SIGMASK:
/* write = 0 done above. */
tmp = data;
if (tmp != 0 && t->p_nlwps > 1) {
lwp_delref(lt);
mutex_enter(t->p_lock);
lt = lwp_find(t, tmp);
if (lt == NULL) {
mutex_exit(t->p_lock);
error = ESRCH;
break;
}
lwp_addref(lt);
mutex_exit(t->p_lock);
}
if (!process_validregs(lt))
error = EINVAL;
else if (write == 1)
error = copyin(addr, &lt->l_sigmask, sizeof(sigset_t));
else
error = copyout(&lt->l_sigmask, addr, sizeof(sigset_t));
break;
#ifdef PT_SETREGS
case PT_SETREGS:
write = 1;

View File

@ -1,4 +1,4 @@
/* $NetBSD: ptrace.h,v 1.55 2017/01/16 21:35:59 kamil Exp $ */
/* $NetBSD: ptrace.h,v 1.56 2017/02/12 06:09:52 kamil Exp $ */
/*-
* Copyright (c) 1984, 1993
@ -55,6 +55,8 @@
#define PT_GET_PROCESS_STATE 18 /* get process state, defined below */
#define PT_SET_SIGINFO 19 /* set signal state, defined below */
#define PT_GET_SIGINFO 20 /* get signal state, defined below */
#define PT_SET_SIGMASK 21 /* set signal mask */
#define PT_GET_SIGMASK 22 /* get signal mask */
#define PT_FIRSTMACH 32 /* for machine-specific requests */
#include <machine/ptrace.h> /* machine-specific requests, if any */
@ -80,7 +82,9 @@
/* 17 */ "PT_GET_EVENT_MASK", \
/* 18 */ "PT_GET_PROCESS_STATE", \
/* 19 */ "PT_SET_SIGINFO", \
/* 20 */ "PT_GET_SIGINFO",
/* 20 */ "PT_GET_SIGINFO", \
/* 20 */ "PT_GET_SIGMASK", \
/* 20 */ "PT_GET_SIGMASK",
/* PT_{G,S}EVENT_MASK */
typedef struct ptrace_event {

View File

@ -1,4 +1,4 @@
/* $NetBSD: t_ptrace_wait.c,v 1.69 2017/01/27 16:43:07 kamil Exp $ */
/* $NetBSD: t_ptrace_wait.c,v 1.70 2017/02/12 06:09:52 kamil Exp $ */
/*-
* Copyright (c) 2016 The NetBSD Foundation, Inc.
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: t_ptrace_wait.c,v 1.69 2017/01/27 16:43:07 kamil Exp $");
__RCSID("$NetBSD: t_ptrace_wait.c,v 1.70 2017/02/12 06:09:52 kamil Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -6583,6 +6583,370 @@ ATF_TC_BODY(signal10, tc)
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TC(getsigmask1);
ATF_TC_HEAD(getsigmask1, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify that plain PT_SET_SIGMASK can be called");
}
ATF_TC_BODY(getsigmask1, tc)
{
const int exitval = 5;
const int sigval = SIGSTOP;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
int status;
#endif
sigset_t mask;
printf("Before forking process PID=%d\n", getpid());
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
printf("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
printf("Before exiting of the child process\n");
_exit(exitval);
}
printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_GET_SIGMASK\n");
ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &mask, 0) != -1);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_exited(status, exitval);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TC(getsigmask2);
ATF_TC_HEAD(getsigmask2, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify that PT_SET_SIGMASK reports correct mask from tracee");
}
ATF_TC_BODY(getsigmask2, tc)
{
const int exitval = 5;
const int sigval = SIGSTOP;
const int sigmasked = SIGTRAP;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
int status;
#endif
sigset_t mask;
sigset_t expected_mask;
ATF_REQUIRE(sigemptyset(&mask) == 0);
ATF_REQUIRE(sigemptyset(&expected_mask) == 0);
ATF_REQUIRE(sigaddset(&expected_mask, sigmasked) == 0);
printf("Before forking process PID=%d\n", getpid());
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
printf("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
sigaddset(&mask, sigmasked);
sigprocmask(SIG_BLOCK, &mask, NULL);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
printf("Before exiting of the child process\n");
_exit(exitval);
}
printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_GET_SIGMASK\n");
ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &mask, 0) != -1);
ATF_REQUIRE(memcmp(&mask, &expected_mask, sizeof(sigset_t)) == 0);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_exited(status, exitval);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TC(setsigmask1);
ATF_TC_HEAD(setsigmask1, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify that plain PT_SET_SIGMASK can be called with empty mask");
}
ATF_TC_BODY(setsigmask1, tc)
{
const int exitval = 5;
const int sigval = SIGSTOP;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
int status;
#endif
sigset_t mask;
ATF_REQUIRE(sigemptyset(&mask) == 0);
printf("Before forking process PID=%d\n", getpid());
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
printf("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
printf("Before exiting of the child process\n");
_exit(exitval);
}
printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_SET_SIGMASK for empty mask\n");
ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_exited(status, exitval);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TC(setsigmask2);
ATF_TC_HEAD(setsigmask2, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify that sigmask is preserved between PT_GET_SIGMASK and "
"PT_SET_SIGMASK");
}
ATF_TC_BODY(setsigmask2, tc)
{
const int exitval = 5;
const int sigval = SIGSTOP;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
int status;
#endif
sigset_t new_mask;
sigset_t mask;
ATF_REQUIRE(sigemptyset(&new_mask) == 0);
ATF_REQUIRE(sigemptyset(&mask) == 0);
ATF_REQUIRE(sigaddset(&mask, SIGINT) == 0);
printf("Before forking process PID=%d\n", getpid());
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
printf("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
printf("Before exiting of the child process\n");
_exit(exitval);
}
printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
printf("Before calling PT_GET_SIGMASK to store it in new_mask\n");
ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &new_mask, 0) != -1);
ATF_REQUIRE(memcmp(&mask, &new_mask, sizeof(sigset_t)) == 0);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_exited(status, exitval);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TC(setsigmask3);
ATF_TC_HEAD(setsigmask3, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify that sigmask is preserved between PT_GET_SIGMASK, process "
"resumed and PT_SET_SIGMASK");
}
ATF_TC_BODY(setsigmask3, tc)
{
const int exitval = 5;
const int sigval = SIGSTOP;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
int status;
#endif
sigset_t new_mask;
sigset_t mask;
ATF_REQUIRE(sigemptyset(&new_mask) == 0);
ATF_REQUIRE(sigemptyset(&mask) == 0);
ATF_REQUIRE(sigaddset(&mask, SIGINT) == 0);
printf("Before forking process PID=%d\n", getpid());
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
printf("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
printf("Before exiting of the child process\n");
_exit(exitval);
}
printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_GET_SIGMASK to store it in new_mask\n");
ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &new_mask, 0) != -1);
ATF_REQUIRE(memcmp(&mask, &new_mask, sizeof(sigset_t)) == 0);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_exited(status, exitval);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TC(setsigmask4);
ATF_TC_HEAD(setsigmask4, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify that new sigmask is visible in tracee");
}
ATF_TC_BODY(setsigmask4, tc)
{
const int exitval = 5;
const int sigval = SIGSTOP;
pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
int status;
#endif
sigset_t mask;
sigset_t expected_mask;
ATF_REQUIRE(sigemptyset(&mask) == 0);
ATF_REQUIRE(sigemptyset(&expected_mask) == 0);
ATF_REQUIRE(sigaddset(&expected_mask, SIGINT) == 0);
printf("Before forking process PID=%d\n", getpid());
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
printf("Before calling PT_TRACE_ME from child %d\n", getpid());
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
printf("Before raising %s from child\n", strsignal(sigval));
FORKEE_ASSERT(raise(sigval) == 0);
sigprocmask(0, NULL, &mask);
FORKEE_ASSERT
(memcmp(&mask, &expected_mask, sizeof(sigset_t)) == 0);
printf("Before exiting of the child process\n");
_exit(exitval);
}
printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_stopped(status, sigval);
printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &expected_mask, 0) != -1);
printf("Before resuming the child process where it left off and "
"without signal to be sent\n");
ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
validate_status_exited(status, exitval);
printf("Before calling %s() for the child\n", TWAIT_FNAME);
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}
ATF_TP_ADD_TCS(tp)
{
setvbuf(stdout, NULL, _IONBF, 0);
@ -6696,5 +7060,13 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, signal9);
ATF_TP_ADD_TC(tp, signal10);
ATF_TP_ADD_TC(tp, getsigmask1);
ATF_TP_ADD_TC(tp, getsigmask2);
ATF_TP_ADD_TC(tp, setsigmask1);
ATF_TP_ADD_TC(tp, setsigmask2);
ATF_TP_ADD_TC(tp, setsigmask3);
ATF_TP_ADD_TC(tp, setsigmask4);
return atf_no_error();
}