PR_SET_PDEATHSIG sets a parent death signal that the calling process
will get when its parent thread dies, even when the result of getppid()
doesn't change because the calling process is reparented to a different
thread in the same parent process. When managing multiple processes, a
process-based parent death signal is much more useful. E.g., to avoid
stray child processes.

PR_SET_PDEATHSIG_PROC sets a process-based death signal. Unlike
PR_SET_PDEATHSIG, this is inherited across fork to allow killing a whole
subtree without race conditions.

This can be used for sandboxing when combined with a seccomp filter.

There have been previous attempts to support this by changing the
behavior of PR_SET_PDEATHSIG. However, that would break existing
applications. See https://marc.info/?l=linux-kernel&m=117621804801689
and https://bugzilla.kernel.org/show_bug.cgi?id=43300

Signed-off-by: Jürg Billeter <j...@bitron.ch>
---

Previous discussion: https://patchwork.kernel.org/patch/9945315/

 fs/exec.c                    |  1 +
 include/linux/sched/signal.h |  3 +++
 include/uapi/linux/prctl.h   |  4 ++++
 kernel/cred.c                |  1 +
 kernel/exit.c                |  4 ++++
 kernel/fork.c                |  2 ++
 kernel/sys.c                 | 11 +++++++++++
 security/apparmor/lsm.c      |  1 +
 security/selinux/hooks.c     |  1 +
 9 files changed, 28 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index ac34d9724684..7045f0223140 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1334,6 +1334,7 @@ void setup_new_exec(struct linux_binprm * bprm)
        if (bprm->secureexec) {
                /* Make sure parent cannot signal privileged process. */
                current->pdeath_signal = 0;
+               current->signal->pdeath_signal_proc = 0;
 
                /*
                 * For secureexec, reset the stack limit to sane default to
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index 2a0dd40b15db..c5c137e5ef39 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -103,6 +103,9 @@ struct signal_struct {
        int                     group_stop_count;
        unsigned int            flags; /* see SIGNAL_* flags below */
 
+       /* The signal sent when the parent dies: */
+       int                     pdeath_signal_proc;
+
        /*
         * PR_SET_CHILD_SUBREAPER marks a process, like a service
         * manager, to re-parent orphan (double-forking) child processes
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index a8d0759a9e40..04508e81d4f2 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -197,4 +197,8 @@ struct prctl_mm_map {
 # define PR_CAP_AMBIENT_LOWER          3
 # define PR_CAP_AMBIENT_CLEAR_ALL      4
 
+/* Process-based variant of PDEATHSIG */
+#define PR_SET_PDEATHSIG_PROC          48
+#define PR_GET_PDEATHSIG_PROC          49
+
 #endif /* _LINUX_PRCTL_H */
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf03657e71c..0192a94670e1 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -448,6 +448,7 @@ int commit_creds(struct cred *new)
                if (task->mm)
                        set_dumpable(task->mm, suid_dumpable);
                task->pdeath_signal = 0;
+               task->signal->pdeath_signal_proc = 0;
                smp_wmb();
        }
 
diff --git a/kernel/exit.c b/kernel/exit.c
index 3481ababd06a..9b6fbb0128d7 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -635,6 +635,10 @@ static void reparent_leader(struct task_struct *father, 
struct task_struct *p,
        if (unlikely(p->exit_state == EXIT_DEAD))
                return;
 
+       if (p->signal->pdeath_signal_proc)
+               group_send_sig_info(p->signal->pdeath_signal_proc,
+                                   SEND_SIG_NOINFO, p);
+
        /* We don't want people slaying init. */
        p->exit_signal = SIGCHLD;
 
diff --git a/kernel/fork.c b/kernel/fork.c
index 10646182440f..264936c367e3 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1415,6 +1415,8 @@ static int copy_signal(unsigned long clone_flags, struct 
task_struct *tsk)
 
        mutex_init(&sig->cred_guard_mutex);
 
+       sig->pdeath_signal_proc = current->signal->pdeath_signal_proc;
+
        return 0;
 }
 
diff --git a/kernel/sys.c b/kernel/sys.c
index 9aebc2935013..dcb9a535404e 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2206,6 +2206,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, 
unsigned long, arg3,
        case PR_GET_PDEATHSIG:
                error = put_user(me->pdeath_signal, (int __user *)arg2);
                break;
+       case PR_SET_PDEATHSIG_PROC:
+               if (!valid_signal(arg2)) {
+                       error = -EINVAL;
+                       break;
+               }
+               me->signal->pdeath_signal_proc = arg2;
+               break;
+       case PR_GET_PDEATHSIG_PROC:
+               error = put_user(me->signal->pdeath_signal_proc,
+                                (int __user *)arg2);
+               break;
        case PR_GET_DUMPABLE:
                error = get_dumpable(me->mm);
                break;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 72b915dfcaf7..98cd937c337d 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -689,6 +689,7 @@ static void apparmor_bprm_committing_creds(struct 
linux_binprm *bprm)
        aa_inherit_files(bprm->cred, current->files);
 
        current->pdeath_signal = 0;
+       current->signal->pdeath_signal_proc = 0;
 
        /* reset soft limits and set hard limits for the new label */
        __aa_transition_rlimits(label, new_ctx->label);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f5d304736852..19d97d5acdb9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2547,6 +2547,7 @@ static void selinux_bprm_committing_creds(struct 
linux_binprm *bprm)
 
        /* Always clear parent death signal on SID transitions. */
        current->pdeath_signal = 0;
+       current->signal->pdeath_signal_proc = 0;
 
        /* Check whether the new SID can inherit resource limits from the old
         * SID.  If not, reset all soft limits to the lower of the current
-- 
2.14.1

Reply via email to