Plan 9 sysexec clears pending notes (unlike posix which leaves pending signals active across exec). So if fork+exec is followed immediately by postnote("kill"), there is a window in sysexec between the last sleep and the clearing of up->nnote where the note from the parent can be lost. This also triggers another bug: exec clears up->nnote but does not clear up->notepending, and therefore the first system call in the child process which calls sleep() will be wrongly terminated with Eintr. The parent process can detect when the child's exec has succeeded, by opening a close-on-exec file before the fork, and waiting for it to be closed before sending a note. (The Go language runtime, for example, does this.) To eliminate the exec-postnote race in this case, this patch moves the close-on-exec code to a later point in the sysexec function, after pending notes have been cleared. The race could be eliminated more simply by not clearing up->nnote on exec, but that would change the semantics of exec and possibly disturb some existing programs. Reference: /n/sources/patch/exec-postnote-race Date: Thu Apr 5 13:36:20 GMT 2018 Signed-off-by: miller@hamnavoe.com --- /sys/src/9/port/sysproc.c Thu Apr 5 13:19:42 2018 +++ /sys/src/9/port/sysproc.c Thu Apr 5 13:19:38 2018 @@ -421,13 +421,6 @@ } } - /* - * Close on exec - */ - f = up->fgrp; - for(i=0; i<=f->maxfd; i++) - fdclose(i, CCEXEC); - /* Text. Shared. Attaches to cache image if possible */ /* attachimage returns a locked cache image */ img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT); @@ -480,6 +473,7 @@ flushmmu(); qlock(&up->debug); up->nnote = 0; + up->notepending = 0; up->notify = 0; up->notified = 0; up->privatemem = 0; @@ -487,6 +481,13 @@ qunlock(&up->debug); if(up->hang) up->procctl = Proc_stopme; + + /* + * Close on exec + */ + f = up->fgrp; + for(i=0; i<=f->maxfd; i++) + fdclose(i, CCEXEC); return execregs(entry, ssize, nargs); }