Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752086AbdI2McP convert rfc822-to-8bit (ORCPT ); Fri, 29 Sep 2017 08:32:15 -0400 Received: from nov-007-i608.relay.mailchannels.net ([46.232.183.162]:17838 "EHLO nov-007-i608.relay.mailchannels.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751567AbdI2McO (ORCPT ); Fri, 29 Sep 2017 08:32:14 -0400 X-Sender-Id: novatrend|x-authuser|juerg@bitron.ch X-Sender-Id: novatrend|x-authuser|juerg@bitron.ch X-MC-Relay: Neutral X-MailChannels-SenderId: novatrend|x-authuser|juerg@bitron.ch X-MailChannels-Auth-Id: novatrend X-Print-Reaction: 5d03a1f07ab501f5_1506688328870_225737969 X-MC-Loop-Signature: 1506688328870:2786751521 X-MC-Ingress-Time: 1506688328870 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= To: Andrew Morton Cc: Oleg Nesterov , Linus Torvalds , Eric Biederman , Michael Kerrisk , Filipe Brandenburger , David Wilcox , "Adam H . Peterson" , hansecke@gmail.com, linux-kernel@vger.kernel.org, =?UTF-8?q?J=C3=BCrg=20Billeter?= Subject: [RESEND PATCH] prctl: add PR_[GS]ET_PDEATHSIG_PROC Date: Fri, 29 Sep 2017 14:30:58 +0200 Message-Id: <20170929123058.48924-1-j@bitron.ch> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20170909094008.49983-1-j@bitron.ch> References: <20170909094008.49983-1-j@bitron.ch> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-AuthUser: juerg@bitron.ch Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5575 Lines: 161 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 --- 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