]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: avoid fork/exit race in plan9
authorRichard Miller <miller.research@gmail.com>
Fri, 25 Mar 2016 12:50:35 +0000 (12:50 +0000)
committerDavid du Colombier <0intro@gmail.com>
Fri, 25 Mar 2016 15:04:45 +0000 (15:04 +0000)
There's a race between runtime.goexitsall killing all OS processes
of a go program in order to exit, and runtime.newosproc forking a
new one.  If the new process has been created but not yet stored
its pid in m.procid, it will not be killed by goexitsall and
deadlock results.

This CL prevents the race by making the newly forked process
check whether the program is exiting.  It also prevents a
potential "shoot-out" if multiple goroutines call Exit at
the same time, which could possibly lead to two processes
killing each other and leaving the rest deadlocked.

Change-Id: I3170b4a62d2461f6b029b3d6aad70373714ed53e
Reviewed-on: https://go-review.googlesource.com/21135
Run-TryBot: David du Colombier <0intro@gmail.com>
Reviewed-by: Marvin Stenger <marvin.stenger94@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David du Colombier <0intro@gmail.com>
src/runtime/os1_plan9.go

index b0b05bb7d73c5a13c6535af1abf72804d95d51b4..2c257442ba45de25ae60aba6ebbb04fb535580c2 100644 (file)
@@ -35,6 +35,9 @@ func sigblock() {
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
+       if atomic.Load(&exiting) != 0 {
+               exits(&emptystatus[0])
+       }
        // Mask all SSE floating-point exceptions
        // when running on the 64-bit kernel.
        setfpmasks()
@@ -148,15 +151,20 @@ func itoa(buf []byte, val uint64) []byte {
 }
 
 var goexits = []byte("go: exit ")
+var emptystatus = []byte("\x00")
+var exiting uint32
 
 func goexitsall(status *byte) {
        var buf [_ERRMAX]byte
+       if !atomic.Cas(&exiting, 0, 1) {
+               return
+       }
        getg().m.locks++
        n := copy(buf[:], goexits)
        n = copy(buf[n:], gostringnocopy(status))
        pid := getpid()
        for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink {
-               if mp.procid != pid {
+               if mp.procid != 0 && mp.procid != pid {
                        postnote(mp.procid, buf[:])
                }
        }
@@ -189,7 +197,7 @@ func postnote(pid uint64, msg []byte) int {
 func exit(e int) {
        var status []byte
        if e == 0 {
-               status = []byte("\x00")
+               status = emptystatus
        } else {
                // build error string
                var tmp [32]byte