aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/cpuset.c
diff options
context:
space:
mode:
authorPaul Jackson <pj@sgi.com>2006-09-29 05:01:48 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-29 12:18:25 -0400
commit181b64803661209cda64e5e874ad75f373a69de8 (patch)
tree0e0ee378b98e5ee72aeae428b1f155d031fe2597 /kernel/cpuset.c
parent683e91cbd0582cb8e63daaf0429e0a62be9cc421 (diff)
[PATCH] cpuset: fix obscure attach_task vs exiting race
Fix obscure race condition in kernel/cpuset.c attach_task() code. There is basically zero chance of anyone accidentally being harmed by this race. It requires a special 'micro-stress' load and a special timing loop hacks in the kernel to hit in less than an hour, and even then you'd have to hit it hundreds or thousands of times, followed by some unusual and senseless cpuset configuration requests, including removing the top cpuset, to cause any visibly harm affects. One could, with perhaps a few days or weeks of such effort, get the reference count on the top cpuset below zero, and manage to crash the kernel by asking to remove the top cpuset. I found it by code inspection. The race was introduced when 'the_top_cpuset_hack' was introduced, and one piece of code was not updated. An old check for a possibly null task cpuset pointer needed to be changed to a check for a task marked PF_EXITING. The pointer can't be null anymore, thanks to the_top_cpuset_hack (documented in kernel/cpuset.c). But the task could have gone into PF_EXITING state after it was found in the task_list scan. If a task is PF_EXITING in this code, it is possible that its task->cpuset pointer is pointing to the top cpuset due to the_top_cpuset_hack, rather than because the top_cpuset was that tasks last valid cpuset. In that case, the wrong cpuset reference counter would be decremented. The fix is trivial. Instead of failing the system call if the tasks cpuset pointer is null here, fail it if the task is in PF_EXITING state. The code for 'the_top_cpuset_hack' that changes an exiting tasks cpuset to the top_cpuset is done without locking, so could happen at anytime. But it is done during the exit handling, after the PF_EXITING flag is set. So if we verify that a task is still not PF_EXITING after we copy out its cpuset pointer (into 'oldcs', below), we know that 'oldcs' is not one of these hack references to the top_cpuset. Signed-off-by: Paul Jackson <pj@sgi.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/cpuset.c')
-rw-r--r--kernel/cpuset.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index cc0395d7eba1..8c3c400cce91 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -1225,7 +1225,12 @@ static int attach_task(struct cpuset *cs, char *pidbuf, char **ppathbuf)
1225 1225
1226 task_lock(tsk); 1226 task_lock(tsk);
1227 oldcs = tsk->cpuset; 1227 oldcs = tsk->cpuset;
1228 if (!oldcs) { 1228 /*
1229 * After getting 'oldcs' cpuset ptr, be sure still not exiting.
1230 * If 'oldcs' might be the top_cpuset due to the_top_cpuset_hack
1231 * then fail this attach_task(), to avoid breaking top_cpuset.count.
1232 */
1233 if (tsk->flags & PF_EXITING) {
1229 task_unlock(tsk); 1234 task_unlock(tsk);
1230 mutex_unlock(&callback_mutex); 1235 mutex_unlock(&callback_mutex);
1231 put_task_struct(tsk); 1236 put_task_struct(tsk);