]> err.no Git - linux-2.6/commitdiff
[PATCH] cpuset: fork hook fix
authorPaul Jackson <pj@sgi.com>
Sun, 8 Jan 2006 09:01:53 +0000 (01:01 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 9 Jan 2006 04:13:43 +0000 (20:13 -0800)
Fix obscure, never seen in real life, cpuset fork race.  The cpuset_fork()
call in fork.c was setting up the correct task->cpuset pointer after the
tasklist_lock was dropped, which briefly exposed the newly forked process with
an unsafe (copied from parent without locks or usage counter increment) cpuset
pointer.

In theory, that exposed cpuset pointer could have been pointing at a cpuset
that was already freed and removed, and in theory another task that had been
sitting on the tasklist_lock waiting to scan the task list could have raced
down the entire tasklist, found our new child at the far end, and dereferenced
that bogus cpuset pointer.

To fix, setup up the correct cpuset pointer in the new child by calling
cpuset_fork() before the new task is linked into the tasklist, and with that,
add a fork failure case, to dereference that cpuset, if the fork fails along
the way, after cpuset_fork() was called.

Had to remove a BUG_ON() from cpuset_exit(), because it was no longer valid -
the call to cpuset_exit() from a failed fork would not have PF_EXITING set.

Signed-off-by: Paul Jackson <pj@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
kernel/cpuset.c
kernel/fork.c

index 3ea63da11d712ca353ad06351ce511ef9c1ca1a8..d9349cc48b95d2b0273a7b4b120c58e083b69bd7 100644 (file)
@@ -1821,15 +1821,13 @@ void cpuset_fork(struct task_struct *child)
  *
  * We don't need to task_lock() this reference to tsk->cpuset,
  * because tsk is already marked PF_EXITING, so attach_task() won't
- * mess with it.
+ * mess with it, or task is a failed fork, never visible to attach_task.
  **/
 
 void cpuset_exit(struct task_struct *tsk)
 {
        struct cpuset *cs;
 
-       BUG_ON(!(tsk->flags & PF_EXITING));
-
        cs = tsk->cpuset;
        tsk->cpuset = NULL;
 
index 7fe3adfa65cb54025eda3093be23086a876435d4..7992ee759d89a7e451f898213f7fa39e315160c4 100644 (file)
@@ -972,12 +972,13 @@ static task_t *copy_process(unsigned long clone_flags,
        p->io_context = NULL;
        p->io_wait = NULL;
        p->audit_context = NULL;
+       cpuset_fork(p);
 #ifdef CONFIG_NUMA
        p->mempolicy = mpol_copy(p->mempolicy);
        if (IS_ERR(p->mempolicy)) {
                retval = PTR_ERR(p->mempolicy);
                p->mempolicy = NULL;
-               goto bad_fork_cleanup;
+               goto bad_fork_cleanup_cpuset;
        }
 #endif
 
@@ -1148,7 +1149,6 @@ static task_t *copy_process(unsigned long clone_flags,
        total_forks++;
        write_unlock_irq(&tasklist_lock);
        proc_fork_connector(p);
-       cpuset_fork(p);
        retval = 0;
 
 fork_out:
@@ -1180,7 +1180,9 @@ bad_fork_cleanup_security:
 bad_fork_cleanup_policy:
 #ifdef CONFIG_NUMA
        mpol_free(p->mempolicy);
+bad_fork_cleanup_cpuset:
 #endif
+       cpuset_exit(p);
 bad_fork_cleanup:
        if (p->binfmt)
                module_put(p->binfmt->module);