Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137029686
D15106.id41557.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
D15106.id41557.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D15106: Add PROC_PDEATHSIG_SET to procctl interface.
Attached
Detach File
Event Timeline
Log In to Comment