aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Jackson <pj@sgi.com>2006-01-08 04:01:53 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-08 23:13:43 -0500
commitb4b2641843db124637fa3d2cb2101982035dcc82 (patch)
treefe8ed223f2e1828a6d14090711bad1c864aded09
parent59dac16fb95f09253b8086134443abeb439703cd (diff)
[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>
-rw-r--r--kernel/cpuset.c4
-rw-r--r--kernel/fork.c6
2 files changed, 5 insertions, 5 deletions
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 3ea63da11d71..d9349cc48b95 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -1821,15 +1821,13 @@ void cpuset_fork(struct task_struct *child)
1821 * 1821 *
1822 * We don't need to task_lock() this reference to tsk->cpuset, 1822 * We don't need to task_lock() this reference to tsk->cpuset,
1823 * because tsk is already marked PF_EXITING, so attach_task() won't 1823 * because tsk is already marked PF_EXITING, so attach_task() won't
1824 * mess with it. 1824 * mess with it, or task is a failed fork, never visible to attach_task.
1825 **/ 1825 **/
1826 1826
1827void cpuset_exit(struct task_struct *tsk) 1827void cpuset_exit(struct task_struct *tsk)
1828{ 1828{
1829 struct cpuset *cs; 1829 struct cpuset *cs;
1830 1830
1831 BUG_ON(!(tsk->flags & PF_EXITING));
1832
1833 cs = tsk->cpuset; 1831 cs = tsk->cpuset;
1834 tsk->cpuset = NULL; 1832 tsk->cpuset = NULL;
1835 1833
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,
972 p->io_context = NULL; 972 p->io_context = NULL;
973 p->io_wait = NULL; 973 p->io_wait = NULL;
974 p->audit_context = NULL; 974 p->audit_context = NULL;
975 cpuset_fork(p);
975#ifdef CONFIG_NUMA 976#ifdef CONFIG_NUMA
976 p->mempolicy = mpol_copy(p->mempolicy); 977 p->mempolicy = mpol_copy(p->mempolicy);
977 if (IS_ERR(p->mempolicy)) { 978 if (IS_ERR(p->mempolicy)) {
978 retval = PTR_ERR(p->mempolicy); 979 retval = PTR_ERR(p->mempolicy);
979 p->mempolicy = NULL; 980 p->mempolicy = NULL;
980 goto bad_fork_cleanup; 981 goto bad_fork_cleanup_cpuset;
981 } 982 }
982#endif 983#endif
983 984
@@ -1148,7 +1149,6 @@ static task_t *copy_process(unsigned long clone_flags,
1148 total_forks++; 1149 total_forks++;
1149 write_unlock_irq(&tasklist_lock); 1150 write_unlock_irq(&tasklist_lock);
1150 proc_fork_connector(p); 1151 proc_fork_connector(p);
1151 cpuset_fork(p);
1152 retval = 0; 1152 retval = 0;
1153 1153
1154fork_out: 1154fork_out:
@@ -1180,7 +1180,9 @@ bad_fork_cleanup_security:
1180bad_fork_cleanup_policy: 1180bad_fork_cleanup_policy:
1181#ifdef CONFIG_NUMA 1181#ifdef CONFIG_NUMA
1182 mpol_free(p->mempolicy); 1182 mpol_free(p->mempolicy);
1183bad_fork_cleanup_cpuset:
1183#endif 1184#endif
1185 cpuset_exit(p);
1184bad_fork_cleanup: 1186bad_fork_cleanup:
1185 if (p->binfmt) 1187 if (p->binfmt)
1186 module_put(p->binfmt->module); 1188 module_put(p->binfmt->module);