ptrace: don't clear GROUP_STOP_SIGMASK on double-stop
authorOleg Nesterov <oleg@redhat.com>
Mon, 26 Sep 2011 17:06:32 +0000 (19:06 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Nov 2011 17:36:23 +0000 (09:36 -0800)
[This does not correspond to any specific patch in the upstream tree as it was
fixed accidentally by rewriting the code in the 3.1 release]

https://bugzilla.redhat.com/show_bug.cgi?id=740121

1. Luke Macken triggered WARN_ON(!(group_stop & GROUP_STOP_SIGMASK))
   in do_signal_stop().

   This is because do_signal_stop() clears GROUP_STOP_SIGMASK part
   unconditionally but doesn't update it if task_is_stopped().

2. Looking at this problem I noticed that WARN_ON_ONCE(!ptrace) is
   not right, a stopped-but-resumed tracee can clone the untraced
   thread in the SIGNAL_STOP_STOPPED group, the new thread can start
   another group-stop.

   Remove this warning, we need more fixes to make it true.

Reported-by: Luke Macken <lmacken@redhat.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
kernel/signal.c

index 415d85d6f6c637b099826d012e46f70832b1d557..43fee1cf50d01efe3e58a578906b16b6ae92ba07 100644 (file)
@@ -1894,21 +1894,19 @@ static int do_signal_stop(int signr)
                 */
                if (!(sig->flags & SIGNAL_STOP_STOPPED))
                        sig->group_exit_code = signr;
-               else
-                       WARN_ON_ONCE(!task_ptrace(current));
 
                current->group_stop &= ~GROUP_STOP_SIGMASK;
                current->group_stop |= signr | gstop;
                sig->group_stop_count = 1;
                for (t = next_thread(current); t != current;
                     t = next_thread(t)) {
-                       t->group_stop &= ~GROUP_STOP_SIGMASK;
                        /*
                         * Setting state to TASK_STOPPED for a group
                         * stop is always done with the siglock held,
                         * so this check has no races.
                         */
                        if (!(t->flags & PF_EXITING) && !task_is_stopped(t)) {
+                               t->group_stop &= ~GROUP_STOP_SIGMASK;
                                t->group_stop |= signr | gstop;
                                sig->group_stop_count++;
                                signal_wake_up(t, 0);