[patch] wait4 SMP race fix (2.2.* && 2.3.*)

Andrea Arcangeli (andrea@suse.de)
Fri, 1 Oct 1999 21:49:11 +0200 (CEST)


In wait4(2) there's an SMP race that may lead to an userspace deadlock
where the parent miss a wakeup from the child and so then the parent hangs
forever in wait4().

The problem can happen only if the parent masked SIGCHLD before calling
wait4() (exactly as bash does) so there won't be any signal pending and
the wakeup of the parent will be done only through the chldexit wq.

CPU0 CPU1
---- ----
fork()
reschedule the child
block SIGCHLD
wait4()
do_exit()
add to wait_chldexit wq
check all chids
all chids are running so
enter the sleep/wait path
task->state = TASK_ZOMBIE;
wakeup chldexit wq
task->state = INTERRUPTIBLE;
/* we invalidated the wakeup!! */
schedule()
deadlock

Here it is the fix against 2.2.13pre14:

--- 2.2.13pre14/kernel/exit.c Tue Sep 28 18:32:39 1999
+++ /tmp/exit.c Fri Oct 1 20:16:57 1999
@@ -433,6 +433,13 @@
add_wait_queue(&current->wait_chldexit,&wait);
repeat:
flag = 0;
+
+ /* The interruptible state must be set before looking at the
+ childs. This because we want to catch any racy exit from
+ the childs as do_exit() may run under us. The following
+ read_lock will enforce SMP ordering at the CPU level. */
+ current->state = TASK_INTERRUPTIBLE;
+
read_lock(&tasklist_lock);
for (p = current->p_cptr ; p ; p = p->p_osptr) {
if (pid>0) {
@@ -499,13 +506,13 @@
retval = -ERESTARTSYS;
if (signal_pending(current))
goto end_wait4;
- current->state=TASK_INTERRUPTIBLE;
schedule();
goto repeat;
}
retval = -ECHILD;
end_wait4:
remove_wait_queue(&current->wait_chldexit,&wait);
+ current->state = TASK_RUNNING;
return retval;
}

I have not the confirm my above patch fixes the deadlock but I am quite
optimistic it will 8).

The patch goes fine on the top of 2.3.18*.

Andrea

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/