Commit 8c86d03b authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Stefan Bader

signal: Always notice exiting tasks

BugLink: https://bugs.launchpad.net/bugs/1818813

commit 35634ffa upstream.

Recently syzkaller was able to create unkillablle processes by
creating a timer that is delivered as a thread local signal on SIGHUP,
and receiving SIGHUP SA_NODEFERER.  Ultimately causing a loop
failing to deliver SIGHUP but always trying.

Upon examination it turns out part of the problem is actually most of
the solution.  Since 2.5 signal delivery has found all fatal signals,
marked the signal group for death, and queued SIGKILL in every threads
thread queue relying on signal->group_exit_code to preserve the
information of which was the actual fatal signal.

The conversion of all fatal signals to SIGKILL results in the
synchronous signal heuristic in next_signal kicking in and preferring
SIGHUP to SIGKILL.  Which is especially problematic as all
fatal signals have already been transformed into SIGKILL.

Instead of dequeueing signals and depending upon SIGKILL to
be the first signal dequeued, first test if the signal group
has already been marked for death.  This guarantees that
nothing in the signal queue can prevent a process that needs
to exit from exiting.

Cc: stable@vger.kernel.org
Tested-by: default avatarDmitry Vyukov <dvyukov@google.com>
Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Ref: ebf5ebe3 ("[PATCH] signal-fixes-2.5.59-A4")
History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.gitSigned-off-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarJuerg Haefliger <juergh@canonical.com>
Signed-off-by: default avatarKhalid Elmously <khalid.elmously@canonical.com>
parent 8c49bac8
...@@ -2198,6 +2198,11 @@ int get_signal(struct ksignal *ksig) ...@@ -2198,6 +2198,11 @@ int get_signal(struct ksignal *ksig)
goto relock; goto relock;
} }
/* Has this task already been marked for death? */
ksig->info.si_signo = signr = SIGKILL;
if (signal_group_exit(signal))
goto fatal;
for (;;) { for (;;) {
struct k_sigaction *ka; struct k_sigaction *ka;
...@@ -2293,6 +2298,7 @@ int get_signal(struct ksignal *ksig) ...@@ -2293,6 +2298,7 @@ int get_signal(struct ksignal *ksig)
continue; continue;
} }
fatal:
spin_unlock_irq(&sighand->siglock); spin_unlock_irq(&sighand->siglock);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment