Add PROC_PDEATHSIG_SET to procctl interface.
Allow processes to request the delivery of a signal upon death of their parent process. Supposed consumer of the feature is PostgreSQL. Submitted by: Thomas Munro Reviewed by: jilles, mjg MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D15106
This commit is contained in:
parent
919015a42e
commit
b940886338
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd November 21, 2017
|
||||
.Dd April 18, 2018
|
||||
.Dt PROCCTL 2
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -391,6 +391,37 @@ otherwise.
|
||||
See the note about sysctl
|
||||
.Dv kern.trap_enotcap
|
||||
above, which gives independent global control of signal delivery.
|
||||
.It Dv PROC_PDEATHSIG_SET
|
||||
Request the delivery of a signal when the parent of the calling
|
||||
process exits.
|
||||
.Fa idtype
|
||||
must be
|
||||
.Dv P_PID
|
||||
and
|
||||
.Fa id
|
||||
must be the either caller's pid or zero, with no difference in effect.
|
||||
The value is cleared for child processes
|
||||
and when executing set-user-ID or set-group-ID binaries.
|
||||
.Fa arg
|
||||
must point to a value of type
|
||||
.Vt int
|
||||
indicating the signal
|
||||
that should be delivered to the caller.
|
||||
Use zero to cancel a previously requested signal delivery.
|
||||
.It Dv PROC_PDEATHSIG_GET
|
||||
Query the current signal number that will be delivered when the parent
|
||||
of the calling process exits.
|
||||
.Fa idtype
|
||||
must be
|
||||
.Dv P_PID
|
||||
and
|
||||
.Fa id
|
||||
must be the either caller's pid or zero, with no difference in effect.
|
||||
.Fa arg
|
||||
must point to a memory location that can hold a value of type
|
||||
.Vt int .
|
||||
If signal delivery has not been requested, it will contain zero
|
||||
on return.
|
||||
.El
|
||||
.Sh NOTES
|
||||
Disabling tracing on a process should not be considered a security
|
||||
@ -487,6 +518,15 @@ parameter for the
|
||||
or
|
||||
.Dv PROC_TRAPCAP_CTL
|
||||
request is invalid.
|
||||
.It Bq Er EINVAL
|
||||
The
|
||||
.Dv PROC_PDEATHSIG_SET
|
||||
or
|
||||
.Dv PROC_PDEATHSIG_GET
|
||||
request referenced an unsupported
|
||||
.Fa id ,
|
||||
.Fa idtype
|
||||
or invalid signal number.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr dtrace 1 ,
|
||||
@ -506,3 +546,8 @@ function appeared in
|
||||
The reaper facility is based on a similar feature of Linux and
|
||||
DragonflyBSD, and first appeared in
|
||||
.Fx 10.2 .
|
||||
The
|
||||
.Dv PROC_PDEATHSIG_SET
|
||||
facility is based on the prctl(PR_SET_PDEATHSIG, ...) feature of Linux,
|
||||
and first appeared in
|
||||
.Fx 12.0 .
|
||||
|
@ -3352,7 +3352,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
||||
union {
|
||||
struct procctl_reaper_pids32 rp;
|
||||
} x32;
|
||||
int error, error1, flags;
|
||||
int error, error1, flags, signum;
|
||||
|
||||
switch (uap->com) {
|
||||
case PROC_SPROTECT:
|
||||
@ -3390,6 +3390,15 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
data = &flags;
|
||||
break;
|
||||
case PROC_PDEATHSIG_SET:
|
||||
error = copyin(uap->data, &signum, sizeof(signum));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
data = &signum;
|
||||
break;
|
||||
case PROC_PDEATHSIG_GET:
|
||||
data = &signum;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
@ -3410,6 +3419,10 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
|
||||
if (error == 0)
|
||||
error = copyout(&flags, uap->data, sizeof(flags));
|
||||
break;
|
||||
case PROC_PDEATHSIG_GET:
|
||||
if (error == 0)
|
||||
error = copyout(&signum, uap->data, sizeof(signum));
|
||||
break;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
@ -522,6 +522,10 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
|
||||
credential_changing |= will_transition;
|
||||
#endif
|
||||
|
||||
/* Don't inherit PROC_PDEATHSIG_SET value if setuid/setgid. */
|
||||
if (credential_changing)
|
||||
imgp->proc->p_pdeathsig = 0;
|
||||
|
||||
if (credential_changing &&
|
||||
#ifdef CAPABILITY_MODE
|
||||
((oldcred->cr_flags & CRED_FLAG_CAPMODE) == 0) &&
|
||||
|
@ -480,6 +480,12 @@ exit1(struct thread *td, int rval, int signo)
|
||||
PROC_LOCK(q->p_reaper);
|
||||
pksignal(q->p_reaper, SIGCHLD, ksi1);
|
||||
PROC_UNLOCK(q->p_reaper);
|
||||
} else if (q->p_pdeathsig > 0) {
|
||||
/*
|
||||
* The child asked to received a signal
|
||||
* when we exit.
|
||||
*/
|
||||
kern_psignal(q, q->p_pdeathsig);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -520,6 +526,13 @@ exit1(struct thread *td, int rval, int signo)
|
||||
*/
|
||||
while ((q = LIST_FIRST(&p->p_orphans)) != NULL) {
|
||||
PROC_LOCK(q);
|
||||
/*
|
||||
* If we are the real parent of this process
|
||||
* but it has been reparented to a debugger, then
|
||||
* check if it asked for a signal when we exit.
|
||||
*/
|
||||
if (q->p_pdeathsig > 0)
|
||||
kern_psignal(q, q->p_pdeathsig);
|
||||
CTR2(KTR_PTRACE, "exit: pid %d, clearing orphan %d", p->p_pid,
|
||||
q->p_pid);
|
||||
clear_orphan(q);
|
||||
|
@ -431,7 +431,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
||||
struct procctl_reaper_pids rp;
|
||||
struct procctl_reaper_kill rk;
|
||||
} x;
|
||||
int error, error1, flags;
|
||||
int error, error1, flags, signum;
|
||||
|
||||
switch (uap->com) {
|
||||
case PROC_SPROTECT:
|
||||
@ -467,6 +467,15 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
data = &flags;
|
||||
break;
|
||||
case PROC_PDEATHSIG_SET:
|
||||
error = copyin(uap->data, &signum, sizeof(signum));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
data = &signum;
|
||||
break;
|
||||
case PROC_PDEATHSIG_GET:
|
||||
data = &signum;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
@ -486,6 +495,10 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
|
||||
if (error == 0)
|
||||
error = copyout(&flags, uap->data, sizeof(flags));
|
||||
break;
|
||||
case PROC_PDEATHSIG_GET:
|
||||
if (error == 0)
|
||||
error = copyout(&signum, uap->data, sizeof(signum));
|
||||
break;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
@ -527,6 +540,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
||||
struct pgrp *pg;
|
||||
struct proc *p;
|
||||
int error, first_error, ok;
|
||||
int signum;
|
||||
bool tree_locked;
|
||||
|
||||
switch (com) {
|
||||
@ -537,10 +551,33 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
|
||||
case PROC_REAP_KILL:
|
||||
case PROC_TRACE_STATUS:
|
||||
case PROC_TRAPCAP_STATUS:
|
||||
case PROC_PDEATHSIG_SET:
|
||||
case PROC_PDEATHSIG_GET:
|
||||
if (idtype != P_PID)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
switch (com) {
|
||||
case PROC_PDEATHSIG_SET:
|
||||
signum = *(int *)data;
|
||||
p = td->td_proc;
|
||||
if ((id != 0 && id != p->p_pid) ||
|
||||
(signum != 0 && !_SIG_VALID(signum)))
|
||||
return (EINVAL);
|
||||
PROC_LOCK(p);
|
||||
p->p_pdeathsig = signum;
|
||||
PROC_UNLOCK(p);
|
||||
return (0);
|
||||
case PROC_PDEATHSIG_GET:
|
||||
p = td->td_proc;
|
||||
if (id != 0 && id != p->p_pid)
|
||||
return (EINVAL);
|
||||
PROC_LOCK(p);
|
||||
*(int *)data = p->p_pdeathsig;
|
||||
PROC_UNLOCK(p);
|
||||
return (0);
|
||||
}
|
||||
|
||||
switch (com) {
|
||||
case PROC_SPROTECT:
|
||||
case PROC_REAP_STATUS:
|
||||
|
@ -91,7 +91,7 @@ _Static_assert(offsetof(struct proc, p_pid) == 0xbc,
|
||||
"struct proc KBI p_pid");
|
||||
_Static_assert(offsetof(struct proc, p_filemon) == 0x3d0,
|
||||
"struct proc KBI p_filemon");
|
||||
_Static_assert(offsetof(struct proc, p_comm) == 0x3e0,
|
||||
_Static_assert(offsetof(struct proc, p_comm) == 0x3e4,
|
||||
"struct proc KBI p_comm");
|
||||
_Static_assert(offsetof(struct proc, p_emuldata) == 0x4b8,
|
||||
"struct proc KBI p_emuldata");
|
||||
@ -111,9 +111,9 @@ _Static_assert(offsetof(struct proc, p_pid) == 0x74,
|
||||
"struct proc KBI p_pid");
|
||||
_Static_assert(offsetof(struct proc, p_filemon) == 0x27c,
|
||||
"struct proc KBI p_filemon");
|
||||
_Static_assert(offsetof(struct proc, p_comm) == 0x288,
|
||||
_Static_assert(offsetof(struct proc, p_comm) == 0x28c,
|
||||
"struct proc KBI p_comm");
|
||||
_Static_assert(offsetof(struct proc, p_emuldata) == 0x314,
|
||||
_Static_assert(offsetof(struct proc, p_emuldata) == 0x318,
|
||||
"struct proc KBI p_emuldata");
|
||||
#endif
|
||||
|
||||
|
@ -624,6 +624,7 @@ struct proc {
|
||||
u_int p_treeflag; /* (e) P_TREE flags */
|
||||
int p_pendingexits; /* (c) Count of pending thread exits. */
|
||||
struct filemon *p_filemon; /* (c) filemon-specific data. */
|
||||
int p_pdeathsig; /* (c) Signal from parent on exit. */
|
||||
/* End area that is zeroed on creation. */
|
||||
#define p_endzero p_magic
|
||||
|
||||
|
@ -51,6 +51,8 @@
|
||||
#define PROC_TRACE_STATUS 8 /* query tracing status */
|
||||
#define PROC_TRAPCAP_CTL 9 /* trap capability errors */
|
||||
#define PROC_TRAPCAP_STATUS 10 /* query trap capability status */
|
||||
#define PROC_PDEATHSIG_SET 11 /* set parent death signal */
|
||||
#define PROC_PDEATHSIG_GET 12 /* get parent death signal */
|
||||
|
||||
/* Operations for PROC_SPROTECT (passed in integer arg). */
|
||||
#define PPROT_OP(x) ((x) & 0xf)
|
||||
|
@ -16,11 +16,13 @@ ATF_TESTS_C+= unix_seqpacket_test
|
||||
ATF_TESTS_C+= unix_passfd_test
|
||||
TEST_METADATA.unix_seqpacket_test+= timeout="15"
|
||||
ATF_TESTS_C+= waitpid_nohang
|
||||
ATF_TESTS_C+= pdeathsig
|
||||
|
||||
ATF_TESTS_SH+= coredump_phnum_test
|
||||
|
||||
BINDIR= ${TESTSDIR}
|
||||
PROGS+= coredump_phnum_helper
|
||||
PROGS+= pdeathsig_helper
|
||||
|
||||
CFLAGS.sys_getrandom+= -I${SRCTOP}/sys/contrib/zstd/lib
|
||||
LIBADD.sys_getrandom+= zstd
|
||||
|
343
tests/sys/kern/pdeathsig.c
Normal file
343
tests/sys/kern/pdeathsig.c
Normal file
@ -0,0 +1,343 @@
|
||||
/*-
|
||||
* Copyright (c) 2018 Thomas Munro
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <assert.h>
|
||||
#include <atf-c.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/procctl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
static void
|
||||
dummy_signal_handler(int signum)
|
||||
{
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(arg_validation);
|
||||
ATF_TC_BODY(arg_validation, tc)
|
||||
{
|
||||
int signum;
|
||||
int rc;
|
||||
|
||||
/* bad signal */
|
||||
signum = 8888;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
ATF_CHECK_EQ(-1, rc);
|
||||
ATF_CHECK_EQ(EINVAL, errno);
|
||||
|
||||
/* bad id type */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(8888, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
ATF_CHECK_EQ(-1, rc);
|
||||
ATF_CHECK_EQ(EINVAL, errno);
|
||||
|
||||
/* bad id (pid that doesn't match mine or zero) */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(P_PID, (((getpid() + 1) % 10) + 100),
|
||||
PROC_PDEATHSIG_SET, &signum);
|
||||
ATF_CHECK_EQ(-1, rc);
|
||||
ATF_CHECK_EQ(EINVAL, errno);
|
||||
|
||||
/* null pointer */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, NULL);
|
||||
ATF_CHECK_EQ(-1, rc);
|
||||
ATF_CHECK_EQ(EFAULT, errno);
|
||||
|
||||
/* good (pid == 0) */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
ATF_CHECK_EQ(0, rc);
|
||||
|
||||
/* good (pid == my pid) */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(P_PID, getpid(), PROC_PDEATHSIG_SET, &signum);
|
||||
ATF_CHECK_EQ(0, rc);
|
||||
|
||||
/* check that we can read the signal number back */
|
||||
signum = 0xdeadbeef;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_GET, &signum);
|
||||
ATF_CHECK_EQ(0, rc);
|
||||
ATF_CHECK_EQ(SIGINFO, signum);
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(fork_no_inherit);
|
||||
ATF_TC_BODY(fork_no_inherit, tc)
|
||||
{
|
||||
int status;
|
||||
int signum;
|
||||
int rc;
|
||||
|
||||
/* request a signal on parent death in the parent */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
|
||||
rc = fork();
|
||||
ATF_REQUIRE(rc != -1);
|
||||
if (rc == 0) {
|
||||
/* check that we didn't inherit the setting */
|
||||
signum = 0xdeadbeef;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_GET, &signum);
|
||||
assert(rc == 0);
|
||||
assert(signum == 0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* wait for the child to exit successfully */
|
||||
waitpid(rc, &status, 0);
|
||||
ATF_CHECK_EQ(0, status);
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(exec_inherit);
|
||||
ATF_TC_BODY(exec_inherit, tc)
|
||||
{
|
||||
int status;
|
||||
int rc;
|
||||
|
||||
rc = fork();
|
||||
ATF_REQUIRE(rc != -1);
|
||||
if (rc == 0) {
|
||||
char exec_path[1024];
|
||||
int signum;
|
||||
|
||||
/* compute the path of the helper executable */
|
||||
snprintf(exec_path, sizeof(exec_path), "%s/pdeathsig_helper",
|
||||
atf_tc_get_config_var(tc, "srcdir"));
|
||||
|
||||
/* request a signal on parent death and register a handler */
|
||||
signum = SIGINFO;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
assert(rc == 0);
|
||||
|
||||
/* execute helper program: it asserts that it has the setting */
|
||||
rc = execl(exec_path, exec_path, NULL);
|
||||
assert(rc == 0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* wait for the child to exit successfully */
|
||||
waitpid(rc, &status, 0);
|
||||
ATF_CHECK_EQ(0, status);
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(signal_delivered);
|
||||
ATF_TC_BODY(signal_delivered, tc)
|
||||
{
|
||||
sigset_t sigset;
|
||||
int signum;
|
||||
int rc;
|
||||
int pipe_ca[2];
|
||||
int pipe_cb[2];
|
||||
char buffer;
|
||||
|
||||
rc = pipe(pipe_ca);
|
||||
ATF_REQUIRE(rc == 0);
|
||||
rc = pipe(pipe_cb);
|
||||
ATF_REQUIRE(rc == 0);
|
||||
|
||||
rc = fork();
|
||||
ATF_REQUIRE(rc != -1);
|
||||
if (rc == 0) {
|
||||
rc = fork();
|
||||
assert(rc >= 0);
|
||||
if (rc == 0) {
|
||||
/* process C */
|
||||
signum = SIGINFO;
|
||||
|
||||
/* block signals so we can handle them synchronously */
|
||||
rc = sigfillset(&sigset);
|
||||
assert(rc == 0);
|
||||
rc = sigprocmask(SIG_SETMASK, &sigset, NULL);
|
||||
assert(rc == 0);
|
||||
|
||||
/* register a dummy handler or the kernel will not queue it */
|
||||
signal(signum, dummy_signal_handler);
|
||||
|
||||
/* request a signal on death of our parent B */
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
assert(rc == 0);
|
||||
|
||||
/* tell B that we're ready for it to exit now */
|
||||
rc = write(pipe_cb[1], ".", 1);
|
||||
assert(rc == 1);
|
||||
|
||||
/* wait for B to die and signal us... */
|
||||
signum = 0xdeadbeef;
|
||||
rc = sigwait(&sigset, &signum);
|
||||
assert(rc == 0);
|
||||
assert(signum == SIGINFO);
|
||||
|
||||
/* tell A the test passed */
|
||||
rc = write(pipe_ca[1], ".", 1);
|
||||
assert(rc == 1);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* process B */
|
||||
|
||||
/* wait for C to tell us it is ready for us to exit */
|
||||
rc = read(pipe_cb[0], &buffer, 1);
|
||||
assert(rc == 1);
|
||||
|
||||
/* now we exit so that C gets a signal */
|
||||
_exit(0);
|
||||
}
|
||||
/* process A */
|
||||
|
||||
/* wait for C to tell us the test passed */
|
||||
rc = read(pipe_ca[0], &buffer, 1);
|
||||
ATF_CHECK_EQ(1, rc);
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(signal_delivered_ptrace);
|
||||
ATF_TC_BODY(signal_delivered_ptrace, tc)
|
||||
{
|
||||
sigset_t sigset;
|
||||
int signum;
|
||||
int rc;
|
||||
int pipe_ca[2];
|
||||
int pipe_db[2];
|
||||
int pipe_cd[2];
|
||||
char buffer;
|
||||
int status;
|
||||
|
||||
rc = pipe(pipe_ca);
|
||||
ATF_REQUIRE(rc == 0);
|
||||
rc = pipe(pipe_db);
|
||||
ATF_REQUIRE(rc == 0);
|
||||
rc = pipe(pipe_cd);
|
||||
ATF_REQUIRE(rc == 0);
|
||||
|
||||
rc = fork();
|
||||
ATF_REQUIRE(rc != -1);
|
||||
if (rc == 0) {
|
||||
pid_t c_pid;
|
||||
|
||||
/* process B */
|
||||
|
||||
rc = fork();
|
||||
assert(rc >= 0);
|
||||
if (rc == 0) {
|
||||
/* process C */
|
||||
signum = SIGINFO;
|
||||
|
||||
/* block signals so we can handle them synchronously */
|
||||
rc = sigfillset(&sigset);
|
||||
assert(rc == 0);
|
||||
rc = sigprocmask(SIG_SETMASK, &sigset, NULL);
|
||||
assert(rc == 0);
|
||||
|
||||
/* register a dummy handler or the kernel will not queue it */
|
||||
signal(signum, dummy_signal_handler);
|
||||
|
||||
/* request a signal on parent death and register a handler */
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_SET, &signum);
|
||||
assert(rc == 0);
|
||||
|
||||
/* tell D we are ready for it to attach */
|
||||
rc = write(pipe_cd[1], ".", 1);
|
||||
assert(rc == 1);
|
||||
|
||||
/* wait for B to die and signal us... */
|
||||
signum = 0xdeadbeef;
|
||||
rc = sigwait(&sigset, &signum);
|
||||
assert(rc == 0);
|
||||
assert(signum == SIGINFO);
|
||||
|
||||
/* tell A the test passed */
|
||||
rc = write(pipe_ca[1], ".", 1);
|
||||
assert(rc == 1);
|
||||
_exit(0);
|
||||
}
|
||||
c_pid = rc;
|
||||
|
||||
|
||||
/* fork another process to ptrace C */
|
||||
rc = fork();
|
||||
assert(rc >= 0);
|
||||
if (rc == 0) {
|
||||
|
||||
/* process D */
|
||||
rc = ptrace(PT_ATTACH, c_pid, 0, 0);
|
||||
assert(rc == 0);
|
||||
|
||||
waitpid(c_pid, &status, 0);
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGSTOP);
|
||||
|
||||
rc = ptrace(PT_CONTINUE, c_pid, (caddr_t) 1, 0);
|
||||
assert(rc == 0);
|
||||
|
||||
/* tell B that we're ready for it to exit now */
|
||||
rc = write(pipe_db[1], ".", 1);
|
||||
assert(rc == 1);
|
||||
|
||||
waitpid(c_pid, &status, 0);
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGINFO);
|
||||
|
||||
rc = ptrace(PT_CONTINUE, c_pid, (caddr_t) 1,
|
||||
WSTOPSIG(status));
|
||||
assert(rc == 0);
|
||||
|
||||
ptrace(PT_DETACH, c_pid, 0, 0);
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* wait for D to tell us it is ready for us to exit */
|
||||
rc = read(pipe_db[0], &buffer, 1);
|
||||
assert(rc == 1);
|
||||
|
||||
/* now we exit so that C gets a signal */
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* process A */
|
||||
|
||||
/* wait for C to tell us the test passed */
|
||||
rc = read(pipe_ca[0], &buffer, 1);
|
||||
ATF_CHECK_EQ(1, rc);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, arg_validation);
|
||||
ATF_TP_ADD_TC(tp, fork_no_inherit);
|
||||
ATF_TP_ADD_TC(tp, exec_inherit);
|
||||
ATF_TP_ADD_TC(tp, signal_delivered);
|
||||
ATF_TP_ADD_TC(tp, signal_delivered_ptrace);
|
||||
return (atf_no_error());
|
||||
}
|
50
tests/sys/kern/pdeathsig_helper.c
Normal file
50
tests/sys/kern/pdeathsig_helper.c
Normal file
@ -0,0 +1,50 @@
|
||||
/*-
|
||||
* Copyright (c) 2018 Thomas Munro
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <sys/procctl.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int signum;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* This program is executed by the pdeathsig test
|
||||
* to check if the PROC_PDEATHSIG_SET setting was
|
||||
* inherited.
|
||||
*/
|
||||
signum = 0xdeadbeef;
|
||||
rc = procctl(P_PID, 0, PROC_PDEATHSIG_GET, &signum);
|
||||
assert(rc == 0);
|
||||
assert(signum == SIGINFO);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user