From f7a1132cffbdf99ef1bd270e9b77f928697780a3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 7 Jan 2004 19:46:52 -0800 Subject: [PATCH] Fix subtle fork() race that Ingo noticed. We must not mark the process TASK_STOPPED early, because that might allow a signal to wake it up before we actually got to the "wake_up_forked_process()" state. Total confusion would happen. Make wake_up_forked_process() verify the new world order. --- kernel/fork.c | 12 ++++++++++-- kernel/sched.c | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 0603f230146b1..8cea5daa08a03 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -918,7 +918,14 @@ struct task_struct *copy_process(unsigned long clone_flags, p->thread_info->preempt_count = 1; #endif p->did_exec = 0; - p->state = TASK_UNINTERRUPTIBLE; + + /* + * We mark the process as running here, but have not actually + * inserted it onto the runqueue yet. This guarantees that + * nobody will actually run it, and a signal or other external + * event cannot wake it up and insert it on the runqueue either. + */ + p->state = TASK_RUNNING; copy_flags(clone_flags, p); if (clone_flags & CLONE_IDLETASK) @@ -1209,9 +1216,10 @@ long do_fork(unsigned long clone_flags, set_tsk_thread_flag(p, TIF_SIGPENDING); } - p->state = TASK_STOPPED; if (!(clone_flags & CLONE_STOPPED)) wake_up_forked_process(p); /* do this last */ + else + p->state = TASK_STOPPED; ++total_forks; if (unlikely (trace)) { diff --git a/kernel/sched.c b/kernel/sched.c index e6f2809808864..6f79281c39541 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -684,7 +684,8 @@ void wake_up_forked_process(task_t * p) unsigned long flags; runqueue_t *rq = task_rq_lock(current, &flags); - p->state = TASK_RUNNING; + BUG_ON(p->state != TASK_RUNNING); + /* * We decrease the sleep average of forking parents * and children as well, to keep max-interactive tasks -- 2.53.0