Add prctl to kill descendants on exit
Subsystems (6)
cover Add prctl to kill descendants on exit
This patch adds a new prctl to kill all descendant processes on exit. See commit message for details of the prctl.
This is a replacement of PR_SET_PDEATHSIG_PROC I proposed last year [1]. In the following discussion, Oleg suggested this approach.
The motivation for this is to provide a lightweight mechanism to prevent stray processes. There is also a related Bugzilla entry [2].
PID namespaces can also be used to prevent stray processes, of course. However, they are not quite as lightweight as they typically also require a new mount namespace to be able to mount a new /proc. And they require CAP_SYS_ADMIN. User namespaces can help to gain CAP_SYS_ADMIN, however, that further increases the overhead and the other effects of the user namespace may not be desired.
PID 1 in PID namespaces also exhibits non-standard signal behavior (SIGNAL_UNKILLABLE) [3].
[1] https://lkml.kernel.org/lkml/[email protected]/
[2] https://bugzilla.kernel.org/show_bug.cgi?id=43300
[3] https://lkml.kernel.org/lkml/[email protected]/
Jürg Billeter (1):
prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
fs/exec.c | 6 ++++++ include/linux/sched/signal.h | 3 +++ include/uapi/linux/prctl.h | 4 ++++ kernel/exit.c | 12 ++++++++++++ kernel/sys.c | 11 +++++++++++ security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 3 +++ 7 files changed, 40 insertions(+)
Signature · 1 line
2.19.2
PATCH 1/1 prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)
When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed. PR_SET_CHILD_SUBREAPER can be used to contain orphaned processes.
If a descendant gained privileges, the current process may not be allowed to kill it, and the descendant process will survive. PR_SET_NO_NEW_PRIVS can be used to prevent descendant processes from gaining privileges.
--- fs/exec.c | 6 ++++++ include/linux/sched/signal.h | 3 +++ include/uapi/linux/prctl.h | 4 ++++ kernel/exit.c | 12 ++++++++++++ kernel/sys.c | 11 +++++++++++ security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 3 +++ 7 files changed, 40 insertions(+)
fs/exec.c +6 -0
@@ -1339,6 +1339,12 @@ void setup_new_exec(struct linux_binprm * bprm)/* Make sure parent cannot signal privileged process. */ current->pdeath_signal =0 ; +/* +* Do not send SIGKILL from privileged process as it may +* have been requested by an unprivileged process. +*/ + current->signal->kill_descendants_on_exit =0 ; +/* * For secureexec, reset the stack limit to sane default to * avoid bad behavior from the prior rlimits. This has to
include/linux/sched/signal.h +3 -0
@@ -124,6 +124,9 @@ struct signal_struct { unsigned int is_child_subreaper:1; unsigned int has_child_subreaper:1; +/* Send SIGKILL to descendant processes on exit */ + unsigned int kill_descendants_on_exit:1; +#ifdef CONFIG_POSIX_TIMERS /* POSIX.1b Interval Timers */
include/uapi/linux/prctl.h +4 -0
@@ -198,6 +198,10 @@ struct prctl_mm_map {# define PR_CAP_AMBIENT_LOWER 3# define PR_CAP_AMBIENT_CLEAR_ALL 4 +/* Send SIGKILL to descendant processes on exit */ +#define PR_SET_KILL_DESCENDANTS_ON_EXIT 48 +#define PR_GET_KILL_DESCENDANTS_ON_EXIT 49 +/* arm64 Scalable Vector Extension controls */ /* Flag values must be kept in sync with ptrace NT_ARM_SVE interface */ #define PR_SVE_SET_VL 50/* set task vector length */
kernel/exit.c +12 -0
@@ -694,6 +694,15 @@ static void forget_original_parent(struct task_struct *father,list_splice_tail_init (&father->children, &reaper->children); } +static int kill_descendant_visitor (struct task_struct *p,void *data) +{ +/* This may fail, e.g., when a descendant process gained privileges. */ +group_send_sig_info (SIGKILL ,SEND_SIG_NOINFO , p,PIDTYPE_TGID ); + +/* Always continue walking the process tree. */ +return 1 ; +} +/* * Send signals to all our closest relatives so that they know * to properly mourn us..
@@ -704,6 +713,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead); +if (group_dead && tsk->signal->kill_descendants_on_exit) +walk_process_tree (tsk, kill_descendant_visitor,NULL ); +write_lock_irq (&tasklist_lock);forget_original_parent (tsk, &dead);
kernel/sys.c +11 -0
@@ -2476,6 +2476,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,return -EINVAL ; error =arch_prctl_spec_ctrl_set (me, arg2, arg3);break ; +case PR_SET_KILL_DESCENDANTS_ON_EXIT : +if (arg3 || arg4 || arg5) +return -EINVAL ; + me->signal->kill_descendants_on_exit = !!arg2; +break ; +case PR_GET_KILL_DESCENDANTS_ON_EXIT : +if (arg3 || arg4 || arg5) +return -EINVAL ; + error =put_user (me->signal->kill_descendants_on_exit, + (int __user *)arg2); +break ;default : error = -EINVAL ;break ;
security/apparmor/lsm.c +1 -0
@@ -695,6 +695,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)aa_inherit_files (bprm->cred, current->files); current->pdeath_signal =0 ; + current->signal->kill_descendants_on_exit =0 ;/* reset soft limits and set hard limits for the new label */ __aa_transition_rlimits (label, new_label);
security/selinux/hooks.c +3 -0
@@ -2653,6 +2653,9 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)/* Always clear parent death signal on SID transitions. */ current->pdeath_signal =0 ; +/* Disable SIGKILL requested from before the SID transition. */ + current->signal->kill_descendants_on_exit =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 * task's hard limit and the init task's soft limit.
Signature · 1 line
2.19.2
Re: [PATCH] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
kernel/exit.c static void exit_notify(struct task_struct *tsk, int group_dead) · line 717
Quoted 7 lines · Jürg Billeter
kernel/exit.c +2
-0@@ -704,6 +713,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead); +if (group_dead && tsk->signal->kill_descendants_on_exit) +walk_process_tree (tsk, kill_descendant_visitor,NULL );
Well, this is not exactly right, at least this is suboptimal in that other sub-threads can too call walk_process_tree(kill_descendant_visitor) later for no reason.
Re: [PATCH] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Jürg Billeter
kernel/exit.c +0
-0@@ -704,6 +713,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead);
Quoted 6 lines
kernel/exit.c static void exit_notify(struct task_struct *tsk, int group_dead) · line 717
Quoted 2 lines
kernel/exit.c +2
-0+if (group_dead && tsk->signal->kill_descendants_on_exit) +walk_process_tree (tsk, kill_descendant_visitor,NULL );Well, this is not exactly right, at least this is suboptimal in that other sub-threads can too call
walk_process_tree(kill_descendant_visitor)later for no reason.
Oleg I think I am missing something.
Reading kernel/exit.c I see "group_dead = atomic_dec_and_test(&tsk->signal->live)". Which seems like enough to ensure exactly one task/thread calls walk_process_tree.
Can you explain what I am missing?
Eric
Re: [PATCH] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 9 lines · Eric W. Biederman
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Jürg Billeter
kernel/exit.c +0
-0@@ -704,6 +713,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead);
Quoted 8 lines
Quoted 6 lines
kernel/exit.c static void exit_notify(struct task_struct *tsk, int group_dead) · line 717
Quoted 2 lines
kernel/exit.c +2
-0+if (group_dead && tsk->signal->kill_descendants_on_exit) +walk_process_tree (tsk, kill_descendant_visitor,NULL );Well, this is not exactly right, at least this is suboptimal in that other sub-threads can too call
walk_process_tree(kill_descendant_visitor)later for no reason.Oleg I think I am missing something.
No, it is stupid me who can't read,
Quoted 2 lines
Reading
kernel/exit.cI see "group_dead=atomic_dec_and_test(&tsk->signal->live)". Which seems like enough to ensure exactly one task/thread callswalk_process_tree.
Of course you right, sorry for confusion.
To me it would be more clean to call walk_process_tree(kill_descendant_visitor) unconditionally in find_new_reaper() right before "if (has_child_subreaper)", but then we will need to shift read_lock(tasklist) from walk_process_tree().
So I think the patch is mostly fine, the only problem I can see is that PR_SET_KILL_DESCENDANTS_ON_EXIT can race with PR_SET_CHILD_SUBREAPER, they both need to update the bits in the same word.
Oleg.
Re: [PATCH] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Hi Oleg,
Thanks for the review.
Quoted 3 lines · Oleg Nesterov
To me it would be more clean to call
walk_process_tree(kill_descendant_visitor)unconditionally infind_new_reaper()right before "if (has_child_subreaper)", but then we will need to shiftread_lock(tasklist)fromwalk_process_tree().
Yes, that's the reason why I added the call before the tasklist lock. Let me know if you want me to move the read lock from walk_process_tree() to PR_SET_CHILD_SUBREAPER (the only caller) instead.
Quoted 3 lines
So I think the patch is mostly fine, the only problem I can see is that
PR_SET_KILL_DESCENDANTS_ON_EXITcan race withPR_SET_CHILD_SUBREAPER, they both need to update the bits in the same word.
Good point. I'll make it a regular bool instead of a bitfield for v2, unless you have another approach in mind to fix this.
Jürg
Re: [PATCH] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 10 lines · Jürg Billeter
Quoted 3 lines · Oleg Nesterov
To me it would be more clean to call
walk_process_tree(kill_descendant_visitor)unconditionally infind_new_reaper()right before "if (has_child_subreaper)", but then we will need to shiftread_lock(tasklist)fromwalk_process_tree().Yes, that's the reason why I added the call before the tasklist lock. Let me know if you want me to move the read lock from
walk_process_tree()toPR_SET_CHILD_SUBREAPER(the only caller) instead.
I am fine either way. We can do this later, lets keep your patch simple.
Quoted 5 lines
Quoted 3 lines
So I think the patch is mostly fine, the only problem I can see is that
PR_SET_KILL_DESCENDANTS_ON_EXITcan race withPR_SET_CHILD_SUBREAPER, they both need to update the bits in the same word.Good point. I'll make it a regular bool instead of a bitfield for v2,
Agreed,
Quoted 1 line
unless you have another approach in mind to fix this.
Well, I think that is_child_subreaper/has_child_subreaper and the new kill_descendants_on_exit should live in signal->flags, but we need some cleanups to make this possible, so I agree with the boolean.
Oleg.
Re: [PATCH] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 11 lines · Oleg Nesterov
Quoted 7 lines · Jürg Billeter
Quoted 3 lines · Oleg Nesterov
So I think the patch is mostly fine, the only problem I can see is that
PR_SET_KILL_DESCENDANTS_ON_EXITcan race withPR_SET_CHILD_SUBREAPER, they both need to update the bits in the same word.Good point. I'll make it a regular bool instead of a bitfield for v2,
Agreed,
Is it worth doing something for singal_struct like we did for task_struct with the TASK_PFA_* and atomic_flags?
Signature · 1 line
Kees Cook
v2 cover Add prctl to kill descendants on exit
In reply to Jürg BilleterThis patch adds a new prctl to kill all descendant processes on exit. See commit message for details of the prctl.
This is a replacement of PR_SET_PDEATHSIG_PROC I proposed last year [1]. In the following discussion, Oleg suggested this approach.
The motivation for this is to provide a lightweight mechanism to prevent stray processes. There is also a related Bugzilla entry [2].
PID namespaces can also be used to prevent stray processes, of course. However, they are not quite as lightweight as they typically also require a new mount namespace to be able to mount a new /proc. And they require CAP_SYS_ADMIN. User namespaces can help to gain CAP_SYS_ADMIN, however, that further increases the overhead and the other effects of the user namespace may not be desired.
PID 1 in PID namespaces also exhibits non-standard signal behavior (SIGNAL_UNKILLABLE) [3].
Changes in v2:
- Use bool instead of bitfield to avoid race with PR_SET_CHILD_SUBREAPER
[1] https://lkml.kernel.org/lkml/[email protected]/
[2] https://bugzilla.kernel.org/show_bug.cgi?id=43300
[3] https://lkml.kernel.org/lkml/[email protected]/
Jürg Billeter (1):
prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
fs/exec.c | 6 ++++++ include/linux/sched/signal.h | 3 +++ include/uapi/linux/prctl.h | 4 ++++ kernel/exit.c | 12 ++++++++++++ kernel/sys.c | 11 +++++++++++ security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 3 +++ 7 files changed, 40 insertions(+)
Signature · 1 line
2.19.2
PATCH v2 1/1 prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)
When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed. PR_SET_CHILD_SUBREAPER can be used to contain orphaned processes.
If a descendant gained privileges, the current process may not be allowed to kill it, and the descendant process will survive. PR_SET_NO_NEW_PRIVS can be used to prevent descendant processes from gaining privileges.
--- fs/exec.c | 6 ++++++ include/linux/sched/signal.h | 3 +++ include/uapi/linux/prctl.h | 4 ++++ kernel/exit.c | 12 ++++++++++++ kernel/sys.c | 11 +++++++++++ security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 3 +++ 7 files changed, 40 insertions(+)
fs/exec.c +6 -0
@@ -1343,6 +1343,12 @@ void setup_new_exec(struct linux_binprm * bprm)/* Make sure parent cannot signal privileged process. */ current->pdeath_signal =0 ; +/* +* Do not send SIGKILL from privileged process as it may +* have been requested by an unprivileged process. +*/ + current->signal->kill_descendants_on_exit = false; +/* * For secureexec, reset the stack limit to sane default to * avoid bad behavior from the prior rlimits. This has to
include/linux/sched/signal.h +3 -0
@@ -124,6 +124,9 @@ struct signal_struct { unsigned int is_child_subreaper:1; unsigned int has_child_subreaper:1; +/* Send SIGKILL to descendant processes on exit */ +bool kill_descendants_on_exit; +#ifdef CONFIG_POSIX_TIMERS /* POSIX.1b Interval Timers */
include/uapi/linux/prctl.h +4 -0
@@ -198,6 +198,10 @@ struct prctl_mm_map {# define PR_CAP_AMBIENT_LOWER 3# define PR_CAP_AMBIENT_CLEAR_ALL 4 +/* Send SIGKILL to descendant processes on exit */ +#define PR_SET_KILL_DESCENDANTS_ON_EXIT 48 +#define PR_GET_KILL_DESCENDANTS_ON_EXIT 49 +/* arm64 Scalable Vector Extension controls */ /* Flag values must be kept in sync with ptrace NT_ARM_SVE interface */ #define PR_SVE_SET_VL 50/* set task vector length */
kernel/exit.c +12 -0
@@ -694,6 +694,15 @@ static void forget_original_parent(struct task_struct *father,list_splice_tail_init (&father->children, &reaper->children); } +static int kill_descendant_visitor (struct task_struct *p,void *data) +{ +/* This may fail, e.g., when a descendant process gained privileges. */ +group_send_sig_info (SIGKILL ,SEND_SIG_NOINFO , p,PIDTYPE_TGID ); + +/* Always continue walking the process tree. */ +return 1 ; +} +/* * Send signals to all our closest relatives so that they know * to properly mourn us..
@@ -704,6 +713,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead); +if (group_dead && tsk->signal->kill_descendants_on_exit) +walk_process_tree (tsk, kill_descendant_visitor,NULL ); +write_lock_irq (&tasklist_lock);forget_original_parent (tsk, &dead);
kernel/sys.c +11 -0
@@ -2476,6 +2476,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,return -EINVAL ; error =arch_prctl_spec_ctrl_set (me, arg2, arg3);break ; +case PR_SET_KILL_DESCENDANTS_ON_EXIT : +if (arg3 || arg4 || arg5) +return -EINVAL ; + me->signal->kill_descendants_on_exit = !!arg2; +break ; +case PR_GET_KILL_DESCENDANTS_ON_EXIT : +if (arg3 || arg4 || arg5) +return -EINVAL ; + error =put_user (me->signal->kill_descendants_on_exit, + (int __user *)arg2); +break ;default : error = -EINVAL ;break ;
security/apparmor/lsm.c +1 -0
@@ -798,6 +798,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)aa_inherit_files (bprm->cred, current->files); current->pdeath_signal =0 ; + current->signal->kill_descendants_on_exit = false;/* reset soft limits and set hard limits for the new label */ __aa_transition_rlimits (label, new_label);
security/selinux/hooks.c +3 -0
@@ -2657,6 +2657,9 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)/* Always clear parent death signal on SID transitions. */ current->pdeath_signal =0 ; +/* Disable SIGKILL requested from before the SID transition. */ + current->signal->kill_descendants_on_exit = false; +/* 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 * task's hard limit and the init task's soft limit.
Signature · 1 line
2.19.2
Re: [PATCH v2 1/1] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 8 lines · Jürg Billeter
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
Re: [PATCH v2 1/1] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
In reply to Jürg Billeter* Jürg Billeter:
Quoted 10 lines
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
So this is inherited across regular execve? I'm not sure that's a good idea.
Quoted 3 lines
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed.
PR_SET_CHILD_SUBREAPERcan be used to contain orphaned processes.
For double- or triple-forking daemons, the reparenting will be racy, if I understand things correctly.
Thanks,
Florian
Re: [PATCH v2 1/1] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 10 lines · Florian Weimer
* Jürg Billeter:
Quoted 8 lines
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
Quoted 5 lines
Quoted 2 lines
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
So this is inherited across regular execve? I'm not sure that's a good idea.
Yes, this matches PR_SET_CHILD_SUBREAPER (and other process attributes). Besides consistency and allowing a parent to configure the flag for a spawned process, this is also needed to prevent a process from clearing the flag (in combination with a seccomp filter).
Quoted 7 lines
Quoted 4 lines
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed.
PR_SET_CHILD_SUBREAPERcan be used to contain orphaned processes.For double- or triple-forking daemons, the reparenting will be racy, if I understand things correctly.
Can you please elaborate, if you're concerned about a particular race? As the commit message mentions, for containment this flag can be combined with PR_SET_CHILD_SUBREAPER (and PR_SET_NO_NEW_PRIVS).
Jürg
Re: [PATCH v2 1/1] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
* Jürg Billeter:
Quoted 14 lines · Florian Weimer
Quoted 13 lines
* Jürg Billeter:
Quoted 10 lines
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
Quoted 7 lines
Quoted 2 lines
So this is inherited across regular execve? I'm not sure that's a good idea.
Yes, this matches
PR_SET_CHILD_SUBREAPER(and other process attributes). Besides consistency and allowing a parent to configure the flag for a spawned process, this is also needed to prevent a process from clearing the flag (in combination with a seccomp filter).
I think the semantics of PR_SET_CHILD_SUBREAPER are different, and the behavior makes more sense there.
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed.
PR_SET_CHILD_SUBREAPERcan be used to contain orphaned processes.For double- or triple-forking daemons, the reparenting will be racy, if I understand things correctly.
Can you please elaborate, if you're concerned about a particular race? As the commit message mentions, for containment this flag can be combined with
PR_SET_CHILD_SUBREAPER(andPR_SET_NO_NEW_PRIVS).
Without PR_SET_CHILD_SUBREAPER, if a newly execve'ed daemon performs double/triple forking to disentangle itself from the parent process session, and the parent process which set PR_SET_KILL_DESCENDANTS_ON_EXIT terminates, behavior depends on when exactly the parent process terminates. The daemon process will leak if it has completed its reparenting.
I think this could be sufficiently common that solution is needed here.
Thanks,
Florian
Re: [PATCH v2 1/1] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 19 lines · Florian Weimer
* Jürg Billeter:
Quoted 17 lines · Florian Weimer
Quoted 15 lines
* Jürg Billeter:
Quoted 10 lines
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
So this is inherited across regular execve? I'm not sure that's a good idea.
Quoted 7 lines
Quoted 4 lines
Yes, this matches
PR_SET_CHILD_SUBREAPER(and other process attributes). Besides consistency and allowing a parent to configure the flag for a spawned process, this is also needed to prevent a process from clearing the flag (in combination with a seccomp filter).I think the semantics of
PR_SET_CHILD_SUBREAPERare different, and the behavior makes more sense there.
In my opinion, introducing inconsistency by deviating from the common behavior of retaining process attributes across execve would be more confusing/surprising to users. I don't see why it makes sense for PR_SET_CHILD_SUBREAPER but not for PR_SET_KILL_DESCENDANTS_ON_EXIT.
Also, the main motivation is to provide a subset of PID namespace features to unprivileged processes with a lightweight mechanism. Retaining kill_descendants_on_exit across execve allows very similar usage to PID namespaces: E.g., the parent can set PR_SET_KILL_DESCENDANTS_ON_EXIT and PR_SET_CHILD_SUBREAPER in the child before execve and the spawned init-like executable doesn't need to know about this flag itself, i.e., the same init-like program can function as a leader of a PID namespace or as a subreaper with this extra flag set without code changes.
If the flag was cleared by execve, the program would need to know about this flag and it would be impossible for the parent to lock this down using seccomp.
Quoted 12 lines
Quoted 11 lines
Quoted 7 lines
Quoted 4 lines
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed.
PR_SET_CHILD_SUBREAPERcan be used to contain orphaned processes.For double- or triple-forking daemons, the reparenting will be racy, if I understand things correctly.
Can you please elaborate, if you're concerned about a particular race? As the commit message mentions, for containment this flag can be combined with
PR_SET_CHILD_SUBREAPER(andPR_SET_NO_NEW_PRIVS).
Quoted 8 lines
Without
PR_SET_CHILD_SUBREAPER, if a newly execve'ed daemon performs double/triple forking to disentangle itself from the parent process session, and the parent process which setPR_SET_KILL_DESCENDANTS_ON_EXITterminates, behavior depends on when exactly the parent process terminates. The daemon process will leak if it has completed its reparenting.I think this could be sufficiently common that solution is needed here.
I expect the common case to be that PR_SET_KILL_DESCENDANTS_ON_EXIT will be used together with PR_SET_CHILD_SUBREAPER (and possibly PR_SET_NO_NEW_PRIVS) to prevent stray children. And I don't see a race condition in that case.
PR_SET_KILL_DESCENDANTS_ON_EXIT can be used for non-subreapers but I expect this to be used in more specialized scenarios where the program is designed/known to avoid such race conditions. We could theoretically restrict PR_SET_KILL_DESCENDANTS_ON_EXIT to subreapers but I currently don't see a strong enough reason for this.
Jürg
Quoted 8 lines · Jürg Billeter
This patch adds a new prctl to kill all descendant processes on exit. See commit message for details of the prctl.
This is a replacement of
PR_SET_PDEATHSIG_PROCI proposed last year [1]. In the following discussion, Oleg suggested this approach.The motivation for this is to provide a lightweight mechanism to prevent stray processes. There is also a related Bugzilla entry [2].
Andrew, Eric, does this look good to you as well?
Jürg
v2 cover Add prctl to kill descendants on exit
In reply to Jürg BilleterThis patch adds a new prctl to kill all descendant processes on exit. See commit message for details of the prctl.
This is a replacement of PR_SET_PDEATHSIG_PROC I proposed last year [1]. In the following discussion, Oleg suggested this approach.
The motivation for this is to provide a lightweight mechanism to prevent stray processes. There is also a related Bugzilla entry [2].
PID namespaces can also be used to prevent stray processes, of course. However, they are not quite as lightweight as they typically also require a new mount namespace to be able to mount a new /proc. And they require CAP_SYS_ADMIN. User namespaces can help to gain CAP_SYS_ADMIN, however, that further increases the overhead and the other effects of the user namespace may not be desired.
PID 1 in PID namespaces also exhibits non-standard signal behavior (SIGNAL_UNKILLABLE) [3].
Changes in v2:
- Use bool instead of bitfield to avoid race with PR_SET_CHILD_SUBREAPER
[1] https://lkml.kernel.org/lkml/[email protected]/
[2] https://bugzilla.kernel.org/show_bug.cgi?id=43300
[3] https://lkml.kernel.org/lkml/[email protected]/
Jürg Billeter (1):
prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
fs/exec.c | 6 ++++++ include/linux/sched/signal.h | 3 +++ include/uapi/linux/prctl.h | 4 ++++ kernel/exit.c | 12 ++++++++++++ kernel/sys.c | 11 +++++++++++ security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 3 +++ 7 files changed, 40 insertions(+)
Signature · 1 line
2.20.1
PATCH RESEND v2 1/1 prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)
When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed. PR_SET_CHILD_SUBREAPER can be used to contain orphaned processes.
If a descendant gained privileges, the current process may not be allowed to kill it, and the descendant process will survive. PR_SET_NO_NEW_PRIVS can be used to prevent descendant processes from gaining privileges.
--- fs/exec.c | 6 ++++++ include/linux/sched/signal.h | 3 +++ include/uapi/linux/prctl.h | 4 ++++ kernel/exit.c | 12 ++++++++++++ kernel/sys.c | 11 +++++++++++ security/apparmor/lsm.c | 1 + security/selinux/hooks.c | 3 +++ 7 files changed, 40 insertions(+)
fs/exec.c +6 -0
@@ -1342,6 +1342,12 @@ void setup_new_exec(struct linux_binprm * bprm)/* Make sure parent cannot signal privileged process. */ current->pdeath_signal =0 ; +/* +* Do not send SIGKILL from privileged process as it may +* have been requested by an unprivileged process. +*/ + current->signal->kill_descendants_on_exit = false; +/* * For secureexec, reset the stack limit to sane default to * avoid bad behavior from the prior rlimits. This has to
include/linux/sched/signal.h +3 -0
@@ -124,6 +124,9 @@ struct signal_struct { unsigned int is_child_subreaper:1; unsigned int has_child_subreaper:1; +/* Send SIGKILL to descendant processes on exit */ +bool kill_descendants_on_exit; +#ifdef CONFIG_POSIX_TIMERS /* POSIX.1b Interval Timers */
include/uapi/linux/prctl.h +4 -0
@@ -198,6 +198,10 @@ struct prctl_mm_map {# define PR_CAP_AMBIENT_LOWER 3# define PR_CAP_AMBIENT_CLEAR_ALL 4 +/* Send SIGKILL to descendant processes on exit */ +#define PR_SET_KILL_DESCENDANTS_ON_EXIT 48 +#define PR_GET_KILL_DESCENDANTS_ON_EXIT 49 +/* arm64 Scalable Vector Extension controls */ /* Flag values must be kept in sync with ptrace NT_ARM_SVE interface */ #define PR_SVE_SET_VL 50/* set task vector length */
kernel/exit.c +12 -0
@@ -694,6 +694,15 @@ static void forget_original_parent(struct task_struct *father,list_splice_tail_init (&father->children, &reaper->children); } +static int kill_descendant_visitor (struct task_struct *p,void *data) +{ +/* This may fail, e.g., when a descendant process gained privileges. */ +group_send_sig_info (SIGKILL ,SEND_SIG_NOINFO , p,PIDTYPE_TGID ); + +/* Always continue walking the process tree. */ +return 1 ; +} +/* * Send signals to all our closest relatives so that they know * to properly mourn us..
@@ -704,6 +713,9 @@ static void exit_notify(struct task_struct *tsk, int group_dead)struct task_struct *p, *n;LIST_HEAD (dead); +if (group_dead && tsk->signal->kill_descendants_on_exit) +walk_process_tree (tsk, kill_descendant_visitor,NULL ); +write_lock_irq (&tasklist_lock);forget_original_parent (tsk, &dead);
kernel/sys.c +11 -0
@@ -2485,6 +2485,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,return -EINVAL ; error =PAC_RESET_KEYS (me, arg2);break ; +case PR_SET_KILL_DESCENDANTS_ON_EXIT : +if (arg3 || arg4 || arg5) +return -EINVAL ; + me->signal->kill_descendants_on_exit = !!arg2; +break ; +case PR_GET_KILL_DESCENDANTS_ON_EXIT : +if (arg3 || arg4 || arg5) +return -EINVAL ; + error =put_user (me->signal->kill_descendants_on_exit, + (int __user *)arg2); +break ;default : error = -EINVAL ;break ;
security/apparmor/lsm.c +1 -0
@@ -698,6 +698,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)aa_inherit_files (bprm->cred, current->files); current->pdeath_signal =0 ; + current->signal->kill_descendants_on_exit = false;/* reset soft limits and set hard limits for the new label */ __aa_transition_rlimits (label, new_label);
security/selinux/hooks.c +3 -0
@@ -2510,6 +2510,9 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)/* Always clear parent death signal on SID transitions. */ current->pdeath_signal =0 ; +/* Disable SIGKILL requested from before the SID transition. */ + current->signal->kill_descendants_on_exit = false; +/* 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 * task's hard limit and the init task's soft limit.
Signature · 1 line
2.20.1
Re: [RESEND PATCH v2 1/1] prctl: add PR_{GET,SET}_KILL_DESCENDANTS_ON_EXIT
Quoted 11 lines · On Fri, 18 Jan 2019 14:11:30 +0100 Jürg Billeter <[email protected]> wrote:
This introduces a new thread group flag that can be set by calling
prctl(PR_SET_KILL_DESCENDANTS_ON_EXIT, 1, 0, 0, 0)When a thread group exits with this flag set, it will send SIGKILL to all descendant processes. This can be used to prevent stray child processes.
This flag is cleared on privilege gaining execve(2) to ensure an unprivileged process cannot get a privileged process to send SIGKILL.
Quoted 8 lines
Descendants that are orphaned and reparented to an ancestor of the current process before the current process exits, will not be killed.
PR_SET_CHILD_SUBREAPERcan be used to contain orphaned processes.If a descendant gained privileges, the current process may not be allowed to kill it, and the descendant process will survive.
PR_SET_NO_NEW_PRIVScan be used to prevent descendant processes from gaining privileges.
I don't feel that I'm able to judge the usefulness of this. It would help to have a lot more words right here in this changelog which communicate the value of this change to our users. References are useful, but please don't send people off to chase down mailing list and bugzilla discussions as a substitute for properly describing the feature and its justification.
Some test code in tools/testing/selftests/ would be helpful.
We'll need to update the prctl(2) manpage if we proceed with this.