summaryrefslogtreecommitdiff
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorPaul Jackson <pj@sgi.com>2006-01-08 01:01:53 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-08 20:13:43 -0800
commitb4b2641843db124637fa3d2cb2101982035dcc82 (patch)
treefe8ed223f2e1828a6d14090711bad1c864aded09 /kernel/fork.c
parent59dac16fb95f09253b8086134443abeb439703cd (diff)
downloadlwn-b4b2641843db124637fa3d2cb2101982035dcc82.tar.gz
lwn-b4b2641843db124637fa3d2cb2101982035dcc82.zip
[PATCH] cpuset: fork hook fix
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>
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index 7fe3adfa65cb..7992ee759d89 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -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);