signal: kill the obsolete SIGNAL_UNKILLABLE check in complete_signal()
cover signal: kill the obsolete SIGNAL_UNKILLABLE check in complete_signal()
Quoted 5 lines · Oleg Nesterov
Cough... and on the second thought this patch needs v2. Sorry Andrew, please drop signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch I'll send the updated version.
Quoted 8 lines
With this patch the parent namespace can use any fatal signal (not only SIGKILL) to kill the init process in container. I do not think this is actually bad, but in any case this should not silently come as a side effect. And this is not consistent with SIGNAL_UNKILLABLE/sig_kernel_only() check in
get_signal().Most probably I will just resend this patch as 2/2, while 1/2 will change
sig_task_ignored()because afaics it is not actually right too (albeit not really buggy).
So this replaces signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch 2/3 is actually the same change, just the changelog was updated.
I'll try to send the cleanups tomorrow. sig_task_ignored() doesn't look nice, sig_handler_ignored() must die, SIGNAL_UNKILLABLE checks in sig_task_ignored() and get_signal() should be unified.
And to remind, we need to fix more problems I found yesterday.
Oleg.
PATCH v2 1/3 signal: change sig_task_ignored(force) to take sig_kernel_only() into account
sig_task_ignored(force => true) returns false unless sig_kernel_ignore(). This makes no sense if !sig_kernel_only(), the signal will be dropped in get_signal() anyway.
This patch simplifies the next change and allows us to do more cleanups, in particular we can unify the SIGNAL_UNKILLABLE/sig_kernel_only() check in sig_task_ignored() and get_signal().
The user-visible change is that, since we drop the SIG_DFL signal early, it won't be reported to debugger. I think this is fine, but probably we will change this later, we need to fix this logic anyway. Currently we rely on the fact that (say) send_sigtrap() uses force_sig_info() which clears SIGNAL_UNKILLABLE, and this is really bad.
--- kernel/signal.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/signal.c +1 -1
@@ -72,7 +72,7 @@ static int sig_task_ignored(struct task_struct *t, int sig, bool force) handler =sig_handler (t, sig);if (unlikely (t->signal->flags &SIGNAL_UNKILLABLE ) && - handler ==SIG_DFL && !force) + handler ==SIG_DFL && !(force &&sig_kernel_only (sig)))return 1 ;return sig_handler_ignored (handler, sig);
Signature · 1 line
1.5.5.1
PATCH v2 2/3 signal: kill the obsolete SIGNAL_UNKILLABLE check in complete_signal()
In reply to Oleg Nesterovcomplete_signal() checks SIGNAL_UNKILLABLE before it starts to destroy the thread group, today this is unnecessary and even not 100% correct.
After the previous change we can rely on sig_task_ignored(); sig_fatal(sig) && SIGNAL_UNKILLABLE can only be true if we actually want to kill this task.
And it does not look right. fatal_signal_pending() should always imply that the whole thread group (except ->group_exit_task if it is not NULL) is killed, this check breaks the rule.
This explains WARN_ON(!JOBCTL_STOP_PENDING) in task_participate_group_stop() triggered by the test-case from Dmitry:
int main()
{
int pid = 1;
ptrace(PTRACE_ATTACH, pid, 0, 0);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL);
sleep(1);
return 0;
}do_signal_stop()->signal_group_exit() returns false because SIGNAL_GROUP_EXIT is not set, but task_set_jobctl_pending() checks fatal_signal_pending() and does not set JOBCTL_STOP_PENDING.
The test-case above needs root and (correctly) crashes the kernel, but we can trigger the same warning inside the container or using another test-case:
static int init(void *arg)
{
for (;;)
pause();
}
int main(void)
{
char stack[16 * 1024];
for (;;) {
int pid = clone(init, stack + sizeof(stack)/2,
CLONE_NEWPID | SIGCHLD, NULL);
assert(pid > 0);
assert(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
assert(waitpid(-1, NULL, WSTOPPED) == pid);
assert(ptrace(PTRACE_DETACH, pid, 0, SIGSTOP) == 0);
assert(syscall(__NR_tkill, pid, SIGKILL) == 0);
assert(pid == wait(NULL));
}
}--- kernel/signal.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/signal.c +1 -1
@@ -914,7 +914,7 @@ static void complete_signal(int sig, struct task_struct *p, int group)* then start taking the whole group down immediately. */ if (sig_fatal (p, sig) && - !(signal->flags & (SIGNAL_UNKILLABLE |SIGNAL_GROUP_EXIT )) && + !(signal->flags &SIGNAL_GROUP_EXIT ) && !sigismember (&t->real_blocked, sig) && (sig ==SIGKILL || !t->ptrace)) {/*
Signature · 1 line
1.5.5.1
PATCH v2 3/3 signal: change complete_signal() to use for_each_thread()
In reply to Oleg Nesterovchange complete_signal() to use for_each_thread().
--- kernel/signal.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-)
kernel/signal.c +2 -3
@@ -930,12 +930,11 @@ static void complete_signal(int sig, struct task_struct *p, int group) signal->flags =SIGNAL_GROUP_EXIT ; signal->group_exit_code = sig; signal->group_stop_count =0 ; - t = p; - do { +for_each_thread (p, t) {task_clear_jobctl_pending (t,JOBCTL_PENDING_MASK );sigaddset (&t->pending.signal,SIGKILL );signal_wake_up (t,1 ); - } while_each_thread(p, t); + }return ; } }
Signature · 1 line
1.5.5.1
Quoted 11 lines · On Thu, 5 Nov 2015 21:17:20 +0100 Oleg Nesterov <[email protected]> wrote:
Quoted 10 lines · Oleg Nesterov
Cough... and on the second thought this patch needs v2. Sorry Andrew, please drop signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch I'll send the updated version.
With this patch the parent namespace can use any fatal signal (not only SIGKILL) to kill the init process in container. I do not think this is actually bad, but in any case this should not silently come as a side effect. And this is not consistent with SIGNAL_UNKILLABLE/sig_kernel_only() check in
get_signal().
Quoted 7 lines
Quoted 3 lines
Most probably I will just resend this patch as 2/2, while 1/2 will change
sig_task_ignored()because afaics it is not actually right too (albeit not really buggy).So this replaces signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch 2/3 is actually the same change, just the changelog was updated.
It's awfully late. Do you see any reason why we should jam this into 4.4?
Quoted 18 lines · Andrew Morton
Quoted 15 lines · On Thu, 5 Nov 2015 21:17:20 +0100 Oleg Nesterov <[email protected]> wrote:
Quoted 13 lines · Oleg Nesterov
Cough... and on the second thought this patch needs v2. Sorry Andrew, please drop signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch I'll send the updated version.
With this patch the parent namespace can use any fatal signal (not only SIGKILL) to kill the init process in container. I do not think this is actually bad, but in any case this should not silently come as a side effect. And this is not consistent with SIGNAL_UNKILLABLE/sig_kernel_only() check in
get_signal().Most probably I will just resend this patch as 2/2, while 1/2 will change
sig_task_ignored()because afaics it is not actually right too (albeit not really buggy).
Quoted 5 lines
Quoted 3 lines
So this replaces signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch 2/3 is actually the same change, just the changelog was updated.
It's awfully late. Do you see any reason why we should jam this into 4.4?
No, I think this can wait till 4.5.
Oleg.
Quoted 5 lines · Oleg Nesterov
Quoted 3 lines · Andrew Morton
On Thu, 5 Nov 2015 21:17:20 +0100 Oleg Nesterov <[email protected]> wrote:
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
So this replaces signal-kill-the-obsolete-signal_unkillable-check-in-complete_signal.patch 2/3 is actually the same change, just the changelog was updated.
It's awfully late. Do you see any reason why we should jam this into 4.4?
No, I think this can wait till 4.5.
Oh. Andrew, please ignore this version too. I'll send v3.
Because when I tried to make the promised cleanups I have found yet another ptrace/SIGKILL/SIGNAL_UNKILLABLE bug, and it seems that it needs to change the same code paths again. I need to think.
Dmitry, do you realize how much I hate you? ;)
Oleg.