Commit b4b26418 authored by Paul Jackson's avatar Paul Jackson Committed by Linus Torvalds

[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: default avatarPaul Jackson <pj@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 59dac16f
...@@ -1821,15 +1821,13 @@ void cpuset_fork(struct task_struct *child) ...@@ -1821,15 +1821,13 @@ void cpuset_fork(struct task_struct *child)
* *
* We don't need to task_lock() this reference to tsk->cpuset, * We don't need to task_lock() this reference to tsk->cpuset,
* because tsk is already marked PF_EXITING, so attach_task() won't * 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) void cpuset_exit(struct task_struct *tsk)
{ {
struct cpuset *cs; struct cpuset *cs;
BUG_ON(!(tsk->flags & PF_EXITING));
cs = tsk->cpuset; cs = tsk->cpuset;
tsk->cpuset = NULL; tsk->cpuset = NULL;
......
...@@ -972,12 +972,13 @@ static task_t *copy_process(unsigned long clone_flags, ...@@ -972,12 +972,13 @@ static task_t *copy_process(unsigned long clone_flags,
p->io_context = NULL; p->io_context = NULL;
p->io_wait = NULL; p->io_wait = NULL;
p->audit_context = NULL; p->audit_context = NULL;
cpuset_fork(p);
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
p->mempolicy = mpol_copy(p->mempolicy); p->mempolicy = mpol_copy(p->mempolicy);
if (IS_ERR(p->mempolicy)) { if (IS_ERR(p->mempolicy)) {
retval = PTR_ERR(p->mempolicy); retval = PTR_ERR(p->mempolicy);
p->mempolicy = NULL; p->mempolicy = NULL;
goto bad_fork_cleanup; goto bad_fork_cleanup_cpuset;
} }
#endif #endif
...@@ -1148,7 +1149,6 @@ static task_t *copy_process(unsigned long clone_flags, ...@@ -1148,7 +1149,6 @@ static task_t *copy_process(unsigned long clone_flags,
total_forks++; total_forks++;
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
proc_fork_connector(p); proc_fork_connector(p);
cpuset_fork(p);
retval = 0; retval = 0;
fork_out: fork_out:
...@@ -1180,7 +1180,9 @@ static task_t *copy_process(unsigned long clone_flags, ...@@ -1180,7 +1180,9 @@ static task_t *copy_process(unsigned long clone_flags,
bad_fork_cleanup_policy: bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
mpol_free(p->mempolicy); mpol_free(p->mempolicy);
bad_fork_cleanup_cpuset:
#endif #endif
cpuset_exit(p);
bad_fork_cleanup: bad_fork_cleanup:
if (p->binfmt) if (p->binfmt)
module_put(p->binfmt->module); module_put(p->binfmt->module);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment