From 955cc07dde70415489fb2096eb575654181e21fe Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 29 Jun 2018 14:56:48 -0400 Subject: [PATCH] runtime: remap stack spans with MAP_STACK on OpenBSD 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 TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/runtime/defs_openbsd.go | 1 + src/runtime/defs_openbsd_386.go | 1 + src/runtime/defs_openbsd_amd64.go | 1 + src/runtime/defs_openbsd_arm.go | 1 + src/runtime/os_nonopenbsd.go | 17 +++++++++++ src/runtime/os_openbsd.go | 47 +++++++++++++++++++++++++++++++ src/runtime/stack.go | 6 ++++ 7 files changed, 74 insertions(+) create mode 100644 src/runtime/os_nonopenbsd.go diff --git a/src/runtime/defs_openbsd.go b/src/runtime/defs_openbsd.go index 9ff13dfcbf5..a328d25db3e 100644 --- a/src/runtime/defs_openbsd.go +++ b/src/runtime/defs_openbsd.go @@ -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 diff --git a/src/runtime/defs_openbsd_386.go b/src/runtime/defs_openbsd_386.go index 11855309646..7b956c44f07 100644 --- a/src/runtime/defs_openbsd_386.go +++ b/src/runtime/defs_openbsd_386.go @@ -17,6 +17,7 @@ const ( _MAP_ANON = 0x1000 _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 + _MAP_STACK = 0x4000 _MADV_FREE = 0x6 diff --git a/src/runtime/defs_openbsd_amd64.go b/src/runtime/defs_openbsd_amd64.go index 4bb8eac08f1..0a939057179 100644 --- a/src/runtime/defs_openbsd_amd64.go +++ b/src/runtime/defs_openbsd_amd64.go @@ -17,6 +17,7 @@ const ( _MAP_ANON = 0x1000 _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 + _MAP_STACK = 0x4000 _MADV_FREE = 0x6 diff --git a/src/runtime/defs_openbsd_arm.go b/src/runtime/defs_openbsd_arm.go index 38b77c92d0d..1eea9ad45ae 100644 --- a/src/runtime/defs_openbsd_arm.go +++ b/src/runtime/defs_openbsd_arm.go @@ -17,6 +17,7 @@ const ( _MAP_ANON = 0x1000 _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 + _MAP_STACK = 0x4000 _MADV_FREE = 0x6 diff --git a/src/runtime/os_nonopenbsd.go b/src/runtime/os_nonopenbsd.go new file mode 100644 index 00000000000..e65697bdb31 --- /dev/null +++ b/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) { +} diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index c359ceb2800..73b01daec43 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -80,6 +80,9 @@ var sigset_all = ^sigset(0) // From OpenBSD's const ( + _CTL_KERN = 1 + _KERN_OSREV = 3 + _CTL_HW = 6 _HW_NCPU = 3 _HW_PAGESIZE = 7 @@ -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) { } @@ -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") @@ -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") + } +} diff --git a/src/runtime/stack.go b/src/runtime/stack.go index 648603db35d..c7bfc0434ba 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -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) @@ -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) } } @@ -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()) @@ -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 @@ -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 @@ -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 }