Commit 840965f8 authored by Austin Clements's avatar Austin Clements

runtime: always clear stack barriers on G exit

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/11461Reviewed-by: 's avatarAlex Brainman <alex.brainman@gmail.com>
Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
parent fac7b86a
......@@ -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)
......
// 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()
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment