]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: always clear stack barriers on G exit
authorAustin Clements <austin@google.com>
Wed, 24 Jun 2015 21:13:24 +0000 (17:13 -0400)
committerAustin Clements <austin@google.com>
Mon, 29 Jun 2015 15:02:30 +0000 (15:02 +0000)
Currently the runtime fails to clear a G's stack barriers in gfput if
the G's stack allocation is _FixedStack bytes. This causes the runtime
to panic if the following sequence of events happens:

1) The runtime installs stack barriers on a G.

2) The G exits by calling runtime.Goexit. Since this does not
   necessarily return through the stack barriers installed on the G,
   there may still be untriggered stack barriers left on the G's stack
   in recorded in g.stkbar.

3) The runtime calls gfput to add the exiting G to the free pool. If
   the G's stack allocation is _FixedStack bytes, we fail to clear
   g.stkbar.

4) A new G starts and allocates the G that was just added to the free
   pool.

5) The new G begins to execute and overwrites the stack slots that had
   stack barriers in them.

6) The garbage collector enters mark termination, attempts to remove
   stack barriers from the new G, and finds that they've been
   overwritten.

Fix this by clearing the stack barriers in gfput in the case where it
reuses the stack.

Fixes #11256.

Change-Id: I377c44258900e6bcc2d4b3451845814a8eeb2bcf
Reviewed-on: https://go-review.googlesource.com/11461
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
src/runtime/proc1.go
test/fixedbugs/issue11256.go [new file with mode: 0644]

index 055f10003314dda4fcf8e7baada0b00430227f0e..da0cab40e6de8db8d415445093cb31d1f2e8351d 100644 (file)
@@ -2294,6 +2294,10 @@ func gfput(_p_ *p, gp *g) {
                gp.stackguard0 = 0
                gp.stkbar = nil
                gp.stkbarPos = 0
+       } else {
+               // Reset stack barriers.
+               gp.stkbar = gp.stkbar[:0]
+               gp.stkbarPos = 0
        }
 
        gp.schedlink.set(_p_.gfree)
diff --git a/test/fixedbugs/issue11256.go b/test/fixedbugs/issue11256.go
new file mode 100644 (file)
index 0000000..69fc3e8
--- /dev/null
@@ -0,0 +1,53 @@
+// run
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that stack barriers are reset when a goroutine exits without
+// returning.
+
+package main
+
+import (
+       "runtime"
+       "sync/atomic"
+       "time"
+)
+
+func main() {
+       // Let the garbage collector run concurrently.
+       runtime.GOMAXPROCS(2)
+
+       var x [100][]byte
+
+       for i := range x {
+               var done int32
+
+               go func() {
+                       // Use enough stack to get stack barriers, but
+                       // not so much that we go over _FixedStack.
+                       // There's a very narrow window here on most
+                       // OSs, so we basically can't do anything (not
+                       // even a time.Sleep or a channel).
+                       var buf [1024]byte
+                       buf[0]++
+                       for atomic.LoadInt32(&done) == 0 {
+                               runtime.Gosched()
+                       }
+                       atomic.StoreInt32(&done, 0)
+                       // Exit without unwinding stack barriers.
+                       runtime.Goexit()
+               }()
+
+               // Generate some garbage.
+               x[i] = make([]byte, 1024*1024)
+
+               // Give GC some time to install stack barriers in the G.
+               time.Sleep(50 * time.Microsecond)
+               atomic.StoreInt32(&done, 1)
+               for atomic.LoadInt32(&done) == 1 {
+                       runtime.Gosched()
+               }
+       }
+}