Page MenuHomeFreeBSD

D15106.id41557.diff
No OneTemporary

D15106.id41557.diff

Index: lib/libc/sys/procctl.2
===================================================================
--- lib/libc/sys/procctl.2
+++ lib/libc/sys/procctl.2
@@ -391,6 +391,34 @@
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 id_type
+must be
+.Dv P_PID
+and
+.Fa id
+must be the either caller's pid or 0, with no difference in effect.
+The value is cleared for the children
+of fork() and when executing set-user-ID or set-group-ID binaries.
+.Fa arg
+must point to a value of type 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 id_type
+must be
+.Dv P_PID
+and
+.Fa id
+must be the either caller's pid or 0, with no difference in effect.
+.Fa arg
+must point to a memory location that can hold a value of type 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 +515,12 @@
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 id, id_type or invalid signal number.
.El
.Sh SEE ALSO
.Xr dtrace 1 ,
@@ -506,3 +540,8 @@
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 .
Index: sys/compat/freebsd32/freebsd32_misc.c
===================================================================
--- sys/compat/freebsd32/freebsd32_misc.c
+++ sys/compat/freebsd32/freebsd32_misc.c
@@ -3352,7 +3352,7 @@
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 @@
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 @@
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);
}
Index: sys/kern/kern_exec.c
===================================================================
--- sys/kern/kern_exec.c
+++ sys/kern/kern_exec.c
@@ -522,6 +522,10 @@
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) &&
Index: sys/kern/kern_exit.c
===================================================================
--- sys/kern/kern_exit.c
+++ sys/kern/kern_exit.c
@@ -480,6 +480,12 @@
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 @@
*/
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);
Index: sys/kern/kern_procctl.c
===================================================================
--- sys/kern/kern_procctl.c
+++ sys/kern/kern_procctl.c
@@ -431,7 +431,7 @@
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 @@
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 @@
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 @@
struct pgrp *pg;
struct proc *p;
int error, first_error, ok;
+ int signum;
bool tree_locked;
switch (com) {
@@ -537,10 +551,31 @@
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;
+ if ((id != 0 && id != td->td_proc->p_pid) ||
+ (signum != 0 && !_SIG_VALID(signum)))
+ return (EINVAL);
+ PROC_LOCK(td->td_proc);
+ td->td_proc->p_pdeathsig = signum;
+ PROC_UNLOCK(td->td_proc);
+ return (0);
+ case PROC_PDEATHSIG_GET:
+ if ((id != 0 && id != td->td_proc->p_pid))
+ return (EINVAL);
+ PROC_LOCK(td->td_proc);
+ *(int *)data = td->td_proc->p_pdeathsig;
+ PROC_UNLOCK(td->td_proc);
+ return (0);
+ }
+
switch (com) {
case PROC_SPROTECT:
case PROC_REAP_STATUS:
Index: sys/kern/kern_thread.c
===================================================================
--- sys/kern/kern_thread.c
+++ sys/kern/kern_thread.c
@@ -91,7 +91,7 @@
"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,7 +111,7 @@
"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,
"struct proc KBI p_emuldata");
Index: sys/sys/proc.h
===================================================================
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -624,6 +624,7 @@
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
Index: sys/sys/procctl.h
===================================================================
--- sys/sys/procctl.h
+++ sys/sys/procctl.h
@@ -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)
Index: tests/sys/kern/Makefile
===================================================================
--- tests/sys/kern/Makefile
+++ tests/sys/kern/Makefile
@@ -16,11 +16,13 @@
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
Index: tests/sys/kern/pdeathsig.c
===================================================================
--- /dev/null
+++ tests/sys/kern/pdeathsig.c
@@ -0,0 +1,328 @@
+/*-
+ * 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 <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 volatile sig_atomic_t got_signal = 0;
+
+static void
+my_signal_handler(int signum)
+{
+ if (signum == SIGINFO)
+ got_signal = 1;
+}
+
+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 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);
+ } else {
+ int status;
+
+ /* 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 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);
+ } else {
+ int status;
+
+ /* 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)
+{
+ 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 */
+
+ /* request a signal on parent death and register a handler */
+ signum = SIGINFO;
+ signal(signum, my_signal_handler);
+ 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 parent to die and signal us... */
+ while (got_signal == 0)
+ sleep(1);
+
+ assert(got_signal == 1);
+
+ /* tell A the test passed */
+ rc = write(pipe_ca[1], ".", 1);
+ assert(rc == 1);
+ return;
+ }
+
+ /* 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 */
+ return;
+ }
+ /* 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)
+{
+ 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 */
+
+ /* request a signal on parent death and register a handler */
+ signum = SIGINFO;
+ signal(signum, my_signal_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 the death signal... */
+ while (got_signal == 0)
+ sleep(1);
+
+ assert(got_signal == 1);
+
+ /* tell A the test passed */
+ rc = write(pipe_ca[1], ".", 1);
+ assert(rc == 1);
+ return;
+ }
+ 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);
+
+ return;
+ }
+
+ /* 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 */
+ return;
+ }
+
+ /* 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());
+}
Index: tests/sys/kern/pdeathsig_helper.c
===================================================================
--- /dev/null
+++ tests/sys/kern/pdeathsig_helper.c
@@ -0,0 +1,21 @@
+#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;
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 22, 12:37 AM (16 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25844984
Default Alt Text
D15106.id41557.diff (16 KB)

Event Timeline