diff options
| author | Linus Torvalds <torvalds@home.osdl.org> | 2004-01-07 19:46:52 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2004-01-07 19:46:52 -0800 |
| commit | f7a1132cffbdf99ef1bd270e9b77f928697780a3 (patch) | |
| tree | 740431cfcea4dce89c71ec7a57d76b6b05069ab0 /kernel/fork.c | |
| parent | f6fd6a01cb2eabd8110c15307dcb9fe5fce019e4 (diff) | |
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.
Diffstat (limited to 'kernel/fork.c')
| -rw-r--r-- | kernel/fork.c | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 0603f230146b..8cea5daa08a0 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)) { |
