diff options
author | Roman Gushchin <guro@fb.com> | 2019-04-26 13:59:44 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2019-05-06 11:39:06 -0400 |
commit | cb2c4cd87874a7975b7b8615866b3a87bae10aab (patch) | |
tree | 306f73b6b4208a222e4e8e3902d5de62668eb5cb /kernel | |
parent | 533307dc20a9e84a0687d4ca24aeb669516c0243 (diff) |
cgroup: prevent spurious transition into non-frozen state
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>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/cgroup/freezer.c | 16 | ||||
-rw-r--r-- | kernel/signal.c | 2 |
2 files changed, 6 insertions, 12 deletions
diff --git a/kernel/cgroup/freezer.c b/kernel/cgroup/freezer.c index 3bfbb3c8baf3..c321e768f8d3 100644 --- a/kernel/cgroup/freezer.c +++ b/kernel/cgroup/freezer.c | |||
@@ -139,19 +139,13 @@ void cgroup_leave_frozen(bool always_leave) | |||
139 | cgroup_update_frozen(cgrp); | 139 | cgroup_update_frozen(cgrp); |
140 | WARN_ON_ONCE(!current->frozen); | 140 | WARN_ON_ONCE(!current->frozen); |
141 | current->frozen = false; | 141 | current->frozen = false; |
142 | } else if (!(current->jobctl & JOBCTL_TRAP_FREEZE)) { | ||
143 | spin_lock(¤t->sighand->siglock); | ||
144 | current->jobctl |= JOBCTL_TRAP_FREEZE; | ||
145 | set_thread_flag(TIF_SIGPENDING); | ||
146 | spin_unlock(¤t->sighand->siglock); | ||
142 | } | 147 | } |
143 | spin_unlock_irq(&css_set_lock); | 148 | spin_unlock_irq(&css_set_lock); |
144 | |||
145 | if (unlikely(current->frozen)) { | ||
146 | /* | ||
147 | * If the task remained in the frozen state, | ||
148 | * make sure it won't reach userspace without | ||
149 | * entering the signal handling loop. | ||
150 | */ | ||
151 | spin_lock_irq(¤t->sighand->siglock); | ||
152 | recalc_sigpending(); | ||
153 | spin_unlock_irq(¤t->sighand->siglock); | ||
154 | } | ||
155 | } | 149 | } |
156 | 150 | ||
157 | /* | 151 | /* |
diff --git a/kernel/signal.c b/kernel/signal.c index 095e0fc57b25..16b72f4f14df 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -2514,7 +2514,7 @@ relock: | |||
2514 | */ | 2514 | */ |
2515 | if (unlikely(cgroup_task_frozen(current))) { | 2515 | if (unlikely(cgroup_task_frozen(current))) { |
2516 | spin_unlock_irq(&sighand->siglock); | 2516 | spin_unlock_irq(&sighand->siglock); |
2517 | cgroup_leave_frozen(true); | 2517 | cgroup_leave_frozen(false); |
2518 | goto relock; | 2518 | goto relock; |
2519 | } | 2519 | } |
2520 | 2520 | ||