cgroup: prevent spurious transition into non-frozen state
authorRoman Gushchin <guro@fb.com>
Fri, 26 Apr 2019 17:59:44 +0000 (10:59 -0700)
committerTejun Heo <tj@kernel.org>
Mon, 6 May 2019 15:39:06 +0000 (08:39 -0700)
If freezing of a cgroup races with waking of a task from
the frozen state (like waiting in vfork() or in do_signal_stop()),
a spurious transition of the cgroup state can happen.

The task enters cgroup_leave_frozen(true), the cgroup->nr_frozen_tasks
counter decrements, and the cgroup is switched to the unfrozen state.

To prevent it, let's reserve cgroup_leave_frozen(true) for
terminating processes and use cgroup_leave_frozen(false) otherwise.

To avoid busy-looping in the signal handling loop waiting
for JOBCTL_TRAP_FREEZE set from the cgroup freezing path,
let's do it explicitly in cgroup_leave_frozen(), if the task
is going to stay frozen.

Suggested-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Roman Gushchin <guro@fb.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/cgroup/freezer.c
kernel/signal.c

index 3bfbb3c8baf3e1d93cd9dd98c589d42c52c11e9f..c321e768f8d321123181a4a4800bf815aaad404d 100644 (file)
@@ -139,19 +139,13 @@ void cgroup_leave_frozen(bool always_leave)
                cgroup_update_frozen(cgrp);
                WARN_ON_ONCE(!current->frozen);
                current->frozen = false;
+       } else if (!(current->jobctl & JOBCTL_TRAP_FREEZE)) {
+               spin_lock(&current->sighand->siglock);
+               current->jobctl |= JOBCTL_TRAP_FREEZE;
+               set_thread_flag(TIF_SIGPENDING);
+               spin_unlock(&current->sighand->siglock);
        }
        spin_unlock_irq(&css_set_lock);
-
-       if (unlikely(current->frozen)) {
-               /*
-                * If the task remained in the frozen state,
-                * make sure it won't reach userspace without
-                * entering the signal handling loop.
-                */
-               spin_lock_irq(&current->sighand->siglock);
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-       }
 }
 
 /*
index 095e0fc57b25d9b15f4b15e86ee4ff7198311006..16b72f4f14dfa11071e87e1662906a8053c44326 100644 (file)
@@ -2514,7 +2514,7 @@ relock:
                 */
                if (unlikely(cgroup_task_frozen(current))) {
                        spin_unlock_irq(&sighand->siglock);
-                       cgroup_leave_frozen(true);
+                       cgroup_leave_frozen(false);
                        goto relock;
                }