Skip to contentSkip to conversation

signal: kill the obsolete SIGNAL_UNKILLABLE check in complete_signal()

PATCH In review Oleg Nesterov 2 participants 7 messages Nov 5, 2015 – Nov 6, 2015

lore
Share & tools
lore
#0 lore raw

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.

#1 lore raw

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
index d64efad..87209e5 100644
@@ -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
#2 lore raw

PATCH v2 2/3 signal: kill the obsolete SIGNAL_UNKILLABLE check in complete_signal()

In reply to Oleg Nesterov

complete_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
index 87209e5..7e9f6fa 100644
@@ -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
#3 lore raw

PATCH v2 3/3 signal: change complete_signal() to use for_each_thread()

In reply to Oleg Nesterov

change complete_signal() to use for_each_thread().

---
 kernel/signal.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)
kernel/signal.c +2 -3
index 7e9f6fa..8cdde5f 100644
@@ -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
#4 lore raw
In reply to Oleg Nesterov
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?

#5 lore raw
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.

#6 lore raw
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.