PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
special signal protection for init processes.
This is required for job control in a shell that uses CLONE_NEWPID for
child processes.
This prctl does not allow setting the SIGNAL_UNKILLABLE flag, only
clearing.
Signed-off-by: Jürg Billeter <redacted>
---
include/uapi/linux/prctl.h | 4 ++++
kernel/sys.c | 11 +++++++++++
2 files changed, 15 insertions(+)
On Mon, 2018-07-30 at 12:17 +0200, Oleg Nesterov wrote:
On 07/30, Jürg Billeter wrote:
quoted
This is required for job control in a shell that uses CLONE_NEWPID for
child processes.
Could you explain in more details?
The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, I don't think it's possible to implement the stop action in
a custom SIGTSTP handler.
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
Job control in a shell is just an example. There are other scenarios,
of course, where applications rely on the default actions as described
in signal(7), and PID isolation may be useful. In my opinion, the
kernel support for preventing accidental killing of the "init" process
should really be optional and this new prctl provides this without
breaking backward compatibility.
From: Thomas Gleixner <hidden> Date: 2018-07-30 19:39:41
On Mon, 30 Jul 2018, Jürg Billeter wrote:
On Mon, 2018-07-30 at 12:17 +0200, Oleg Nesterov wrote:
quoted
On 07/30, Jürg Billeter wrote:
quoted
This is required for job control in a shell that uses CLONE_NEWPID for
child processes.
Could you explain in more details?
The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, I don't think it's possible to implement the stop action in
a custom SIGTSTP handler.
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
Job control in a shell is just an example. There are other scenarios,
of course, where applications rely on the default actions as described
in signal(7), and PID isolation may be useful. In my opinion, the
kernel support for preventing accidental killing of the "init" process
should really be optional and this new prctl provides this without
breaking backward compatibility.
This makes sense and exactly that information needs to be in the changelog.
Thanks,
tglx
PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
special signal protection for init processes. This prctl does not allow
setting the SIGNAL_UNKILLABLE flag, only clearing.
The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, it's not possible to implement the stop action in a custom
SIGTSTP handler.
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
There are other scenarios besides job control in a shell where
applications rely on the default actions as described in signal(7) and
PID isolation may be useful. This new prctl makes the signal protection
for "init" processes optional, without breaking backward compatibility.
Signed-off-by: Jürg Billeter <redacted>
---
v2: Hold siglock for PR_SET_KILLABLE, expand commit message.
include/uapi/linux/prctl.h | 4 ++++
kernel/sys.c | 13 +++++++++++++
2 files changed, 17 insertions(+)
PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
special signal protection for init processes. This prctl does not allow
setting the SIGNAL_UNKILLABLE flag, only clearing.
The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
Ah. My question wasn't clear, sorry.
Could you explain your use-case? Why a shell wants to use CLONE_NEWPID?
And what do we actually want in, say, ^Z case? Just stop the child reaper
or may be it would be better to stop the whole pid namespace?
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, it's not possible to implement the stop action in a custom
SIGTSTP handler.
Yes. So may be we actually want to change __isig() paths to use
SEND_SIG_FORCED (this is not that simple), or perhaps we can change
__send_signal() to not drop SIGSTOP sent to itself, or may be we can even
introduce SIG_DFL_EVEN_IF_INIT, I dunno.
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
I can't understand this. An application should be changed anyway to do
PR_SET_KILLABLE?
Let me clarify. I am not arguing with this patch, probably it makes sense in
any case. I am just trying to understand your real motivation for this change.
On Tue, 2018-07-31 at 16:39 +0200, Oleg Nesterov wrote:
On 07/31, Jürg Billeter wrote:
quoted
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
Ah. My question wasn't clear, sorry.
Could you explain your use-case? Why a shell wants to use
CLONE_NEWPID?
To guarantee that there won't be any runaway processes, i.e., ensure
that no descendants (background helper daemons or misbehaving
processes) survive when the child process is terminated. And to prevent
children from killing their ancestors. This is not something that can
be always-on in all shells, but it could be an option for users that
want this control/isolation.
And what do we actually want in, say, ^Z case? Just stop the child reaper
or may be it would be better to stop the whole pid namespace?
Stopping the whole PID namespace would be interesting, however, I think
this should be discussed separately if and when there is a proposal to
support this. For now the process group is stopped, same as without PID
namespaces.
quoted
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, it's not possible to implement the stop action in a custom
SIGTSTP handler.
Yes. So may be we actually want to change __isig() paths to use
SEND_SIG_FORCED (this is not that simple), or perhaps we can change
__send_signal() to not drop SIGSTOP sent to itself, or may be we can even
introduce SIG_DFL_EVEN_IF_INIT, I dunno.
In my opinion, my patch is much simpler and also more general as it
covers all scenarios where regular signal handling is required or
desired for "init" processes, with minimal code changes (after
PR_SET_KILLABLE, binaries that expect SIG_DFL to work can be executed
without changes).
quoted
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
I can't understand this. An application should be changed anyway to do
PR_SET_KILLABLE?
PR_SET_KILLABLE can be called (e.g., by the shell) between clone() and
execve(). (Some applications may have issues running as subreaper but
that's a separate matter, signal handling will work as expected).
Could you explain your use-case? Why a shell wants to use
CLONE_NEWPID?
To guarantee that there won't be any runaway processes, i.e., ensure
that no descendants (background helper daemons or misbehaving
processes) survive when the child process is terminated.
We already have PR_SET_CHILD_SUBREAPER.
Perhaps we can finally add PR_KILL_MY_DESCENDANTS_ON_EXIT? This was already
discussed some time ago, but I can't find the previous discussion... Simple
to implement.
And to prevent
children from killing their ancestors.
OK, this is the only reason for CLONE_NEWPID which I can understand so far.
Not that I understand why this is that useful ;)
quoted
quoted
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, it's not possible to implement the stop action in a custom
SIGTSTP handler.
Yes. So may be we actually want to change __isig() paths to use
SEND_SIG_FORCED (this is not that simple), or perhaps we can change
__send_signal() to not drop SIGSTOP sent to itself, or may be we can even
introduce SIG_DFL_EVEN_IF_INIT, I dunno.
In my opinion, my patch is much simpler and also more general as it
Yes, yes, let me repeat that I am not arguing with your patch, I am just trying
to understand what
quoted
I can't understand this. An application should be changed anyway to do
PR_SET_KILLABLE?
PR_SET_KILLABLE can be called (e.g., by the shell) between clone() and
execve().
On Wed, 2018-08-01 at 16:19 +0200, Oleg Nesterov wrote:
On 07/31, Jürg Billeter wrote:
quoted
quoted
Could you explain your use-case? Why a shell wants to use
CLONE_NEWPID?
To guarantee that there won't be any runaway processes, i.e., ensure
that no descendants (background helper daemons or misbehaving
processes) survive when the child process is terminated.
We already have PR_SET_CHILD_SUBREAPER.
Perhaps we can finally add PR_KILL_MY_DESCENDANTS_ON_EXIT? This was already
discussed some time ago, but I can't find the previous discussion... Simple
to implement.
This would definitely be an option. You mentioned it last October in
the PR_SET_PDEATHSIG_PROC discussion¹. However, as PID namespaces
already exist and appear to be a good fit for the most part, I think it
makes sense to just add the missing pieces to PID namespaces instead of
duplicating part of the PID namespace functionality.
Also, based on Eric's comment in that other discussion about
no_new_privs not being allowed to increase the attack surface,
PR_KILL_MY_DESCENDANTS_ON_EXIT might require CAP_SYS_ADMIN as well (due
to setuid children). In which case the only potential benefit would be
that it still allows the child to kill arbitrary processes, as far as I
can tell.
quoted
And to prevent children from killing their ancestors.
OK, this is the only reason for CLONE_NEWPID which I can understand so far.
Not that I understand why this is that useful ;)
The overall goal is increasing isolation between (some) child processes
and the rest of the system. Isolation from runaway processes and
isolation from signals are independent aspects and it could be useful
to control them independently. However, I also expect it to be common
that both are wanted at the same time.
Jürg
¹ https://lkml.org/lkml/2017/10/5/546
On Wed, 2018-08-01 at 16:19 +0200, Oleg Nesterov wrote:
quoted
On 07/31, Jürg Billeter wrote:
quoted
quoted
Could you explain your use-case? Why a shell wants to use
CLONE_NEWPID?
To guarantee that there won't be any runaway processes, i.e., ensure
that no descendants (background helper daemons or misbehaving
processes) survive when the child process is terminated.
We already have PR_SET_CHILD_SUBREAPER.
Perhaps we can finally add PR_KILL_MY_DESCENDANTS_ON_EXIT? This was already
discussed some time ago, but I can't find the previous discussion... Simple
to implement.
This would definitely be an option. You mentioned it last October in
the PR_SET_PDEATHSIG_PROC discussion¹. However, as PID namespaces
already exist and appear to be a good fit for the most part,
Sure, if CLONE_NEWPID fits your needs you can use it,
I think it
makes sense to just add the missing pieces to PID namespaces instead of
duplicating part of the PID namespace functionality.
Again, I am not arguing with your change.
PR_KILL_MY_DESCENDANTS_ON_EXIT can make sense just like PR_SET_CHILD_SUBREAPER
even if PID namespace functionality implies both. Simply because CLONE_NEWPID
is not necessarily the best tool, if nothing else you do not necessarily want
the pid isolation.
Also, based on Eric's comment in that other discussion about
no_new_privs not being allowed to increase the attack surface,
PR_KILL_MY_DESCENDANTS_ON_EXIT might require CAP_SYS_ADMIN as well (due
to setuid children).
No, no, the exiting parent should simply do group_send_sig_info(SIGKILL)
for every descendant and rely on check_kill_permission().
OK, lets forget it for now.
Oleg.
On Mon, Jul 30, 2018 at 10:01 AM Jürg Billeter [off-list ref] wrote:
PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
special signal protection for init processes.
This is required for job control in a shell that uses CLONE_NEWPID for
child processes.
This prctl does not allow setting the SIGNAL_UNKILLABLE flag, only
clearing.
Signed-off-by: Jürg Billeter <redacted>
---
I don't have an opinion on this patchset otherwise, but should this
prctl maybe block PR_SET_KILLABLE if you're actually the real init
process? This seems like it could potentially lead to weird things.
This code in kernel/fork.c seems to rely on the fact that global init
is SIGNAL_UNKILLABLE, and probably also leads to weirdness if
container init is non-SIGNAL_UNKILLABLE:
/*
* Siblings of global init remain as zombies on exit since they are
* not reaped by their parent (swapper). To solve this and to avoid
* multi-rooted process trees, prevent global and container-inits
* from creating siblings.
*/
if ((clone_flags & CLONE_PARENT) &&
current->signal->flags & SIGNAL_UNKILLABLE)
return ERR_PTR(-EINVAL);
I don't have an opinion on this patchset otherwise, but should this
prctl maybe block PR_SET_KILLABLE if you're actually the real init
process? This seems like it could potentially lead to weird things.
While I don't expect global init to use this, I can't think of a good
reason to disallow it in the kernel. Do you have specific concerns or
is the code in kernel/fork.c the only reason? I prefer avoiding special
cases unless really required.
This code in kernel/fork.c seems to rely on the fact that global init
is SIGNAL_UNKILLABLE, and probably also leads to weirdness if
container init is non-SIGNAL_UNKILLABLE:
Yes, Oleg has mentioned this as well. I have to change copy_process()
to directly check for the PID namespace root process instead of
checking for SIGNAL_UNKILLABLE.
Jürg
I don't have an opinion on this patchset otherwise, but should this
prctl maybe block PR_SET_KILLABLE if you're actually the real init
process? This seems like it could potentially lead to weird things.
While I don't expect global init to use this, I can't think of a good
reason to disallow it in the kernel. Do you have specific concerns or
is the code in kernel/fork.c the only reason?
No, I don't have any other specific concerns.
I prefer avoiding special cases unless really required.
copy_process() currently checks the SIGNAL_UNKILLABLE flag to determine
whether to accept CLONE_PARENT. In preparation for allowing init
processes to opt out of SIGNAL_UNKILLABLE, directly check whether the
process is an init process with is_child_reaper().
Signed-off-by: Jürg Billeter <redacted>
---
kernel/fork.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
special signal protection for init processes. This prctl does not allow
setting the SIGNAL_UNKILLABLE flag, only clearing.
The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, it's not possible to implement the stop action in a custom
SIGTSTP handler.
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
There are other scenarios besides job control in a shell where
applications rely on the default actions as described in signal(7) and
PID isolation may be useful. This new prctl makes the signal protection
for "init" processes optional, without breaking backward compatibility.
Signed-off-by: Jürg Billeter <redacted>
---
include/uapi/linux/prctl.h | 4 ++++
kernel/sys.c | 13 +++++++++++++
2 files changed, 17 insertions(+)
From: Andrew Morton <akpm@linux-foundation.org> Date: 2018-09-06 22:42:12
On Fri, 3 Aug 2018 16:40:21 +0200 Jürg Billeter [off-list ref] wrote:
PR_SET_KILLABLE clears the SIGNAL_UNKILLABLE flag. This allows
CLONE_NEWPID tasks to restore normal signal behavior, opting out of the
special signal protection for init processes. This prctl does not allow
setting the SIGNAL_UNKILLABLE flag, only clearing.
The SIGNAL_UNKILLABLE flag, which is implicitly set for tasks cloned
with CLONE_NEWPID, has the effect of ignoring all signals (from
userspace) if the corresponding handler is set to SIG_DFL. The only
exceptions are SIGKILL and SIGSTOP and they are only accepted if raised
from an ancestor namespace.
SIGINT, SIGQUIT and SIGTSTP are used in job control for ^C, ^\, ^Z.
While a task with the SIGNAL_UNKILLABLE flag could install handlers for
these signals, this is not sufficient to implement a shell that uses
CLONE_NEWPID for child processes:
* As SIGSTOP is ignored when raised from the SIGNAL_UNKILLABLE process
itself, it's not possible to implement the stop action in a custom
SIGTSTP handler.
* Many applications do not install handlers for these signals and
thus, job control won't work properly with unmodified applications.
There are other scenarios besides job control in a shell where
applications rely on the default actions as described in signal(7) and
PID isolation may be useful. This new prctl makes the signal protection
for "init" processes optional, without breaking backward compatibility.
This one is above my pay grade. Eric & Oleg: could you please provide
input?