From 840965f8d7ccad5ac1782e208865e8120f5c080a Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 24 Jun 2015 17:13:24 -0400 Subject: [PATCH] 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/11461 Reviewed-by: Alex Brainman Reviewed-by: Russ Cox --- src/runtime/proc1.go | 4 +++ test/fixedbugs/issue11256.go | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/fixedbugs/issue11256.go diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index 055f100033..da0cab40e6 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -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 index 0000000000..69fc3e8d84 --- /dev/null +++ b/test/fixedbugs/issue11256.go @@ -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() + } + } +}