Skip to content

Commit

Permalink
runtime: remap stack spans with MAP_STACK on OpenBSD
Browse files Browse the repository at this point in the history
OpenBSD 6.4 is going to start requiring that the SP points to memory
that was mapped with MAP_STACK on system call entry, traps, and when
switching to the alternate signal stack [1]. Currently, Go doesn't map
any memory MAP_STACK, so the kernel quickly kills Go processes.

Fix this by remapping the memory that backs stack spans with
MAP_STACK, and re-remapping it without MAP_STACK when it's returned to
the heap.

[1] http://openbsd-archive.7691.n7.nabble.com/stack-register-checking-td338238.html

Fixes #26142.

Change-Id: I656eb84385a22833445d49328bb304f8cdd0e225
Reviewed-on: https://go-review.googlesource.com/121657
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
  • Loading branch information
aclements committed Jun 29, 2018
1 parent a94a390 commit 955cc07
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/runtime/defs_openbsd.go
Expand Up @@ -37,6 +37,7 @@ const (
MAP_ANON = C.MAP_ANON
MAP_PRIVATE = C.MAP_PRIVATE
MAP_FIXED = C.MAP_FIXED
MAP_STACK = C.MAP_STACK

MADV_FREE = C.MADV_FREE

Expand Down
1 change: 1 addition & 0 deletions src/runtime/defs_openbsd_386.go
Expand Up @@ -17,6 +17,7 @@ const (
_MAP_ANON = 0x1000
_MAP_PRIVATE = 0x2
_MAP_FIXED = 0x10
_MAP_STACK = 0x4000

_MADV_FREE = 0x6

Expand Down
1 change: 1 addition & 0 deletions src/runtime/defs_openbsd_amd64.go
Expand Up @@ -17,6 +17,7 @@ const (
_MAP_ANON = 0x1000
_MAP_PRIVATE = 0x2
_MAP_FIXED = 0x10
_MAP_STACK = 0x4000

_MADV_FREE = 0x6

Expand Down
1 change: 1 addition & 0 deletions src/runtime/defs_openbsd_arm.go
Expand Up @@ -17,6 +17,7 @@ const (
_MAP_ANON = 0x1000
_MAP_PRIVATE = 0x2
_MAP_FIXED = 0x10
_MAP_STACK = 0x4000

_MADV_FREE = 0x6

Expand Down
17 changes: 17 additions & 0 deletions src/runtime/os_nonopenbsd.go
@@ -0,0 +1,17 @@
// Copyright 2018 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.

// +build !openbsd

package runtime

// osStackAlloc performs OS-specific initialization before s is used
// as stack memory.
func osStackAlloc(s *mspan) {
}

// osStackFree undoes the effect of osStackAlloc before s is returned
// to the heap.
func osStackFree(s *mspan) {
}
47 changes: 47 additions & 0 deletions src/runtime/os_openbsd.go
Expand Up @@ -80,6 +80,9 @@ var sigset_all = ^sigset(0)

// From OpenBSD's <sys/sysctl.h>
const (
_CTL_KERN = 1
_KERN_OSREV = 3

_CTL_HW = 6
_HW_NCPU = 3
_HW_PAGESIZE = 7
Expand Down Expand Up @@ -109,6 +112,17 @@ func getPageSize() uintptr {
return 0
}

func getOSRev() int32 {
mib := [2]uint32{_CTL_KERN, _KERN_OSREV}
out := uint32(0)
nout := unsafe.Sizeof(out)
ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
if ret >= 0 {
return int32(out)
}
return 0
}

//go:nosplit
func semacreate(mp *m) {
}
Expand Down Expand Up @@ -194,6 +208,7 @@ func newosproc(mp *m) {
func osinit() {
ncpu = getncpu()
physPageSize = getPageSize()
haveMapStack = getOSRev() >= 201805 // OpenBSD 6.3
}

var urandom_dev = []byte("/dev/urandom\x00")
Expand Down Expand Up @@ -286,3 +301,35 @@ func sigdelset(mask *sigset, i int) {

func (c *sigctxt) fixsigcode(sig uint32) {
}

var haveMapStack = false

func osStackAlloc(s *mspan) {
// OpenBSD 6.4+ requires that stacks be mapped with MAP_STACK.
// It will check this on entry to system calls, traps, and
// when switching to the alternate system stack.
//
// This function is called before s is used for any data, so
// it's safe to simply re-map it.
osStackRemap(s, _MAP_STACK)
}

func osStackFree(s *mspan) {
// Undo MAP_STACK.
osStackRemap(s, 0)
}

func osStackRemap(s *mspan, flags int32) {
if !haveMapStack {
// OpenBSD prior to 6.3 did not have MAP_STACK and so
// the following mmap will fail. But it also didn't
// require MAP_STACK (obviously), so there's no need
// to do the mmap.
return
}
a, err := mmap(unsafe.Pointer(s.base()), s.npages*pageSize, _PROT_READ|_PROT_WRITE, _MAP_PRIVATE|_MAP_ANON|_MAP_FIXED|flags, -1, 0)
if err != 0 || uintptr(a) != s.base() {
print("runtime: remapping stack memory ", hex(s.base()), " ", s.npages*pageSize, " a=", a, " err=", err, "\n")
throw("remapping stack memory failed")
}
}
6 changes: 6 additions & 0 deletions src/runtime/stack.go
Expand Up @@ -186,6 +186,7 @@ func stackpoolalloc(order uint8) gclinkptr {
if s.manualFreeList.ptr() != nil {
throw("bad manualFreeList")
}
osStackAlloc(s)
s.elemsize = _FixedStack << order
for i := uintptr(0); i < _StackCacheSize; i += s.elemsize {
x := gclinkptr(s.base() + i)
Expand Down Expand Up @@ -238,6 +239,7 @@ func stackpoolfree(x gclinkptr, order uint8) {
// By not freeing, we prevent step #4 until GC is done.
stackpool[order].remove(s)
s.manualFreeList = 0
osStackFree(s)
mheap_.freeManual(s, &memstats.stacks_inuse)
}
}
Expand Down Expand Up @@ -385,6 +387,7 @@ func stackalloc(n uint32) stack {
if s == nil {
throw("out of memory")
}
osStackAlloc(s)
s.elemsize = uintptr(n)
}
v = unsafe.Pointer(s.base())
Expand Down Expand Up @@ -463,6 +466,7 @@ func stackfree(stk stack) {
if gcphase == _GCoff {
// Free the stack immediately if we're
// sweeping.
osStackFree(s)
mheap_.freeManual(s, &memstats.stacks_inuse)
} else {
// If the GC is running, we can't return a
Expand Down Expand Up @@ -1112,6 +1116,7 @@ func freeStackSpans() {
if s.allocCount == 0 {
list.remove(s)
s.manualFreeList = 0
osStackFree(s)
mheap_.freeManual(s, &memstats.stacks_inuse)
}
s = next
Expand All @@ -1126,6 +1131,7 @@ func freeStackSpans() {
for s := stackLarge.free[i].first; s != nil; {
next := s.next
stackLarge.free[i].remove(s)
osStackFree(s)
mheap_.freeManual(s, &memstats.stacks_inuse)
s = next
}
Expand Down

0 comments on commit 955cc07

Please sign in to comment.