[release-branch.go1.4] runtime: mark stacks with MAP_STACK on openbsd

OpenBSD 6.4+ requires that stack regions be marked with MAP_STACK.
MAP_STACK is permitted (but is a no-op) on OpenBSD 6.3.

Change-Id: I8118f27e6a0feb97ae570cbc8aad461ec20676fa
Reviewed-on: https://go-review.googlesource.com/c/154477
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/runtime/defs_openbsd_386.h b/src/runtime/defs_openbsd_386.h
index 6b77e00..c4b43ae 100644
--- a/src/runtime/defs_openbsd_386.h
+++ b/src/runtime/defs_openbsd_386.h
@@ -14,6 +14,7 @@
 	MAP_ANON	= 0x1000,
 	MAP_PRIVATE	= 0x2,
 	MAP_FIXED	= 0x10,
+	MAP_STACK	= 0x4000,
 
 	MADV_FREE	= 0x6,
 
diff --git a/src/runtime/defs_openbsd_amd64.h b/src/runtime/defs_openbsd_amd64.h
index 761e8e4..263bfeb 100644
--- a/src/runtime/defs_openbsd_amd64.h
+++ b/src/runtime/defs_openbsd_amd64.h
@@ -14,6 +14,7 @@
 	MAP_ANON	= 0x1000,
 	MAP_PRIVATE	= 0x2,
 	MAP_FIXED	= 0x10,
+	MAP_STACK	= 0x4000,
 
 	MADV_FREE	= 0x6,
 
diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h
index adb8d3d..f19751a 100644
--- a/src/runtime/malloc.h
+++ b/src/runtime/malloc.h
@@ -197,6 +197,8 @@
 // if accessed.  Used only for debugging the runtime.
 
 void*	runtime·sysAlloc(uintptr nbytes, uint64 *stat);
+void	runtime·sysMarkStack(void *v, uintptr nbytes);
+void	runtime·sysUnmarkStack(void *v, uintptr nbytes);
 void	runtime·SysFree(void *v, uintptr nbytes, uint64 *stat);
 void	runtime·SysUnused(void *v, uintptr nbytes);
 void	runtime·SysUsed(void *v, uintptr nbytes);
diff --git a/src/runtime/mem_openbsd.c b/src/runtime/mem_openbsd.c
index 31820e5..ac3a2a5 100644
--- a/src/runtime/mem_openbsd.c
+++ b/src/runtime/mem_openbsd.c
@@ -27,6 +27,29 @@
 	return v;
 }
 
+#pragma textflag NOSPLIT
+void
+runtime·sysMarkStack(void *v, uintptr n)
+{
+	void *p;
+
+	// Stack regions on OpenBSD 6.4+ must be marked with MAP_STACK.
+	p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_FIXED|MAP_STACK, -1, 0);
+	if (p == ((void *)-1) || p != v)
+		runtime·throw("runtime: failed to mark stack");
+}
+
+#pragma textflag NOSPLIT
+void
+runtime·sysUnmarkStack(void *v, uintptr n)
+{
+	void *p;
+
+	p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0);
+	if (p == ((void *)-1) || p != v)
+		runtime·throw("runtime: failed to unmark stack");
+}
+
 void
 runtime·SysUnused(void *v, uintptr n)
 {
diff --git a/src/runtime/os_openbsd.c b/src/runtime/os_openbsd.c
index eebaa13..e5484d9 100644
--- a/src/runtime/os_openbsd.c
+++ b/src/runtime/os_openbsd.c
@@ -167,7 +167,10 @@
 
 	param.tf_tcb = (byte*)&mp->tls[0];
 	param.tf_tid = (int32*)&mp->procid;
-	param.tf_stack = stk;
+
+	// Stack pointer must point inside stack area (as marked with MAP_STACK),
+	// rather than at the top of it.
+	param.tf_stack = (void*)((uintptr)stk - sizeof(uintptr));
 
 	oset = runtime·sigprocmask(SIG_SETMASK, sigset_all);
 	ret = runtime·tfork(&param, sizeof(param), mp, mp->g0, runtime·mstart);
diff --git a/src/runtime/stack.c b/src/runtime/stack.c
index cb95572..88344d0 100644
--- a/src/runtime/stack.c
+++ b/src/runtime/stack.c
@@ -71,6 +71,9 @@
 			runtime·throw("bad ref");
 		if(s->freelist != nil)
 			runtime·throw("bad freelist");
+#ifdef GOOS_openbsd
+		runtime·sysMarkStack((void *)(s->start << PageShift), s->npages << PageShift);
+#endif
 		for(i = 0; i < StackCacheSize; i += FixedStack << order) {
 			x = (MLink*)((s->start << PageShift) + i);
 			x->next = s->freelist;
@@ -110,6 +113,9 @@
 		// span is completely free - return to heap
 		runtime·MSpanList_Remove(s);
 		s->freelist = nil;
+#ifdef GOOS_openbsd
+		runtime·sysUnmarkStack((void *)(s->start << PageShift), s->npages << PageShift);
+#endif
 		runtime·MHeap_FreeStack(&runtime·mheap, s);
 	}
 }
@@ -246,6 +252,9 @@
 		s = runtime·MHeap_AllocStack(&runtime·mheap, ROUND(n, PageSize) >> PageShift);
 		if(s == nil)
 			runtime·throw("out of memory");
+#ifdef GOOS_openbsd
+		runtime·sysMarkStack((void *)(s->start << PageShift), s->npages << PageShift);
+#endif
 		v = (byte*)(s->start<<PageShift);
 	}
 	
@@ -307,6 +316,9 @@
 			runtime·printf("%p %p\n", s->start<<PageShift, v);
 			runtime·throw("bad span state");
 		}
+#ifdef GOOS_openbsd
+		runtime·sysUnmarkStack((void *)(s->start << PageShift), s->npages << PageShift);
+#endif
 		runtime·MHeap_FreeStack(&runtime·mheap, s);
 	}
 }