signal: protect SIGNAL_UNKILLABLE from unintentional clearing.
PATCH v2 1/1 signal: protect SIGNAL_UNKILLABLE from unintentional clearing.
Since 00cd5c37af (ptrace: permit ptracing of /sbin/init) we can now trace init processes. init is initially protected with SIGNAL_UNKILLABLE which will prevent fatal signals such as SIGSTOP, but there are a number of paths during tracing where SIGNAL_UNKILLABLE can be implicitly cleared.
This can result in init becoming stoppable/killable after tracing. For example, running:
while true; do kill -STOP 1; done &
strace -p 1
and then stopping strace and the kill loop will result in init being left in state TASK_STOPPED. Sending SIGCONT to init will resume it, but init will now respond to future SIGSTOP signals rather than ignoring them.
Make sure that when setting SIGNAL_STOP_CONTINUED/SIGNAL_STOP_STOPPED that we don't clear SIGNAL_UNKILLABLE.
---
v1->v2:
- dropped set/clear helpers
- added signal_set_stop_flags
include/linux/sched.h | 10 ++++++++++ kernel/signal.c | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-)
include/linux/sched.h +10 -0
@@ -830,6 +830,16 @@ struct signal_struct {#define SIGNAL_UNKILLABLE 0x00000040/* for init: ignore fatal signals */ +#define SIGNAL_STOP_MASK (SIGNAL_CLD_MASK | SIGNAL_STOP_STOPPED | \ + SIGNAL_STOP_CONTINUED) + +static inline void signal_set_stop_flags (struct signal_struct *sig, +unsigned int flags) +{ +WARN_ON (sig->flags & (SIGNAL_GROUP_EXIT |SIGNAL_GROUP_COREDUMP )); + sig->flags = (sig->flags & ~SIGNAL_STOP_MASK ) | flags; +} +/* If true, all threads except ->group_exit_task have pending SIGKILL */ static inline int signal_group_exit (const struct signal_struct *sig) {
kernel/signal.c +2 -2
@@ -346,7 +346,7 @@ static bool task_participate_group_stop(struct task_struct *task)* fresh group stop. Read comment in do_signal_stop() for details. */ if (!sig->group_stop_count && !(sig->flags &SIGNAL_STOP_STOPPED )) { - sig->flags =SIGNAL_STOP_STOPPED ; +signal_set_stop_flags (sig,SIGNAL_STOP_STOPPED );return true; }return false;
@@ -837,7 +837,7 @@ static bool prepare_signal(int sig, struct task_struct *p, bool force)* will take ->siglock, notice SIGNAL_CLD_MASK, and * notify its parent. See get_signal_to_deliver(). */ - signal->flags = why |SIGNAL_STOP_CONTINUED ; +signal_set_stop_flags (signal, why |SIGNAL_STOP_CONTINUED ); signal->group_stop_count =0 ; signal->group_exit_code =0 ; }
Signature · 1 line
2.11.0
Quoted 11 lines · Jamie Iles
This can result in init becoming stoppable/killable after tracing. For example, running:
while true; do kill -STOP 1; done &
strace -p 1and then stopping strace and the kill loop will result in init being left in state
TASK_STOPPED. Sending SIGCONT to init will resume it, but init will now respond to future SIGSTOP signals rather than ignoring them.
and to SIGKILL.
Another problem is that /sbin/init should ignore SIGKILL even when ptraced, but this needs another patch, will try to do tomorrow. And yet another problem with force_sig(), it should probably take ptrace and/or SIGTRAP into account.
Thanks Jamie,