summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-02-20 16:18:05 -0500
committerRuss Cox <rsc@golang.org>2014-02-20 16:18:05 -0500
commited591404c19dffaf84876d8e3d75db926b78a130 (patch)
treebd9764edb7953c1b09a4ac8231c994e370947744
parent50be4ad5a71a5db95c620805be653d41c7d41ebd (diff)
downloadgo-ed591404c19dffaf84876d8e3d75db926b78a130.tar.gz
runtime/debug: add SetPanicOnFault
SetPanicOnFault allows recovery from unexpected memory faults. This can be useful if you are using a memory-mapped file or probing the address space of the current program. LGTM=r R=r CC=golang-codereviews https://codereview.appspot.com/66590044
-rw-r--r--doc/go1.3.txt1
-rw-r--r--src/pkg/runtime/debug/garbage.go11
-rw-r--r--src/pkg/runtime/os_darwin.c4
-rw-r--r--src/pkg/runtime/os_dragonfly.c4
-rw-r--r--src/pkg/runtime/os_freebsd.c4
-rw-r--r--src/pkg/runtime/os_linux.c4
-rw-r--r--src/pkg/runtime/os_netbsd.c4
-rw-r--r--src/pkg/runtime/os_openbsd.c4
-rw-r--r--src/pkg/runtime/os_solaris.c4
-rw-r--r--src/pkg/runtime/os_windows.c2
-rw-r--r--src/pkg/runtime/proc.c1
-rw-r--r--src/pkg/runtime/rdebug.goc5
-rw-r--r--src/pkg/runtime/runtime.h1
-rw-r--r--src/pkg/runtime/runtime_test.go18
14 files changed, 52 insertions, 15 deletions
diff --git a/doc/go1.3.txt b/doc/go1.3.txt
index 31dfd38a1..613d26197 100644
--- a/doc/go1.3.txt
+++ b/doc/go1.3.txt
@@ -8,3 +8,4 @@ testing: add b.RunParallel function (CL 57270043)
misc/benchcmp has been replaced by go tool benchcmp (CL 47980043)
cmd/go, go/build: support .m files (CL 60590044)
unicode: upgrade from Unicode 6.2.0 to 6.3.0 (CL 65400044)
+runtime/debug: add SetPanicOnFault (CL 66590044)
diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go
index a724fdf8f..093e7e7b1 100644
--- a/src/pkg/runtime/debug/garbage.go
+++ b/src/pkg/runtime/debug/garbage.go
@@ -135,3 +135,14 @@ func SetMaxStack(bytes int) int {
func SetMaxThreads(threads int) int {
return setMaxThreads(threads)
}
+
+// SetPanicOnFault controls the runtime's behavior when a program faults
+// at an unexpected (non-nil) address. Such faults are typically caused by
+// bugs such as runtime memory corruption, so the default response is to crash
+// the program. Programs working with memory-mapped files or unsafe
+// manipulation of memory may cause faults at non-nil addresses in less
+// dramatic situations; SetPanicOnFault allows such programs to request
+// that the runtime trigger only a panic, not a crash.
+// SetPanicOnFault applies only to the current goroutine.
+// It returns the previous setting.
+func SetPanicOnFault(enabled bool) bool
diff --git a/src/pkg/runtime/os_darwin.c b/src/pkg/runtime/os_darwin.c
index 5a38a77a3..f2262618d 100644
--- a/src/pkg/runtime/os_darwin.c
+++ b/src/pkg/runtime/os_darwin.c
@@ -436,7 +436,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -444,7 +444,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_dragonfly.c b/src/pkg/runtime/os_dragonfly.c
index ade3adb26..f96ea8948 100644
--- a/src/pkg/runtime/os_dragonfly.c
+++ b/src/pkg/runtime/os_dragonfly.c
@@ -171,7 +171,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -179,7 +179,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_freebsd.c b/src/pkg/runtime/os_freebsd.c
index 442c6d767..8b7b0f626 100644
--- a/src/pkg/runtime/os_freebsd.c
+++ b/src/pkg/runtime/os_freebsd.c
@@ -179,7 +179,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -187,7 +187,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_linux.c b/src/pkg/runtime/os_linux.c
index 130e1059e..b4be9406e 100644
--- a/src/pkg/runtime/os_linux.c
+++ b/src/pkg/runtime/os_linux.c
@@ -220,7 +220,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -228,7 +228,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_netbsd.c b/src/pkg/runtime/os_netbsd.c
index 745818183..f8ae30985 100644
--- a/src/pkg/runtime/os_netbsd.c
+++ b/src/pkg/runtime/os_netbsd.c
@@ -239,7 +239,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -247,7 +247,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_openbsd.c b/src/pkg/runtime/os_openbsd.c
index 45f5040c9..c16276aca 100644
--- a/src/pkg/runtime/os_openbsd.c
+++ b/src/pkg/runtime/os_openbsd.c
@@ -216,7 +216,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -224,7 +224,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_solaris.c b/src/pkg/runtime/os_solaris.c
index 4e212f544..b8cd4d90c 100644
--- a/src/pkg/runtime/os_solaris.c
+++ b/src/pkg/runtime/os_solaris.c
@@ -211,7 +211,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case SIGBUS:
- if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) {
+ if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
@@ -219,7 +219,7 @@ runtime·sigpanic(void)
runtime·printf("unexpected fault address %p\n", g->sigcode1);
runtime·throw("fault");
case SIGSEGV:
- if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) {
+ if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/os_windows.c b/src/pkg/runtime/os_windows.c
index 8815aee9f..a84b4e283 100644
--- a/src/pkg/runtime/os_windows.c
+++ b/src/pkg/runtime/os_windows.c
@@ -348,7 +348,7 @@ runtime·sigpanic(void)
{
switch(g->sig) {
case EXCEPTION_ACCESS_VIOLATION:
- if(g->sigcode1 < 0x1000) {
+ if(g->sigcode1 < 0x1000 || g->paniconfault) {
if(g->sigpc == 0)
runtime·panicstring("call of nil func value");
runtime·panicstring("invalid memory address or nil pointer dereference");
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 7799dc8b5..986136d7e 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -1449,6 +1449,7 @@ goexit0(G *gp)
gp->status = Gdead;
gp->m = nil;
gp->lockedm = nil;
+ gp->paniconfault = 0;
m->curg = nil;
m->lockedg = nil;
if(m->locked & ~LockExternal) {
diff --git a/src/pkg/runtime/rdebug.goc b/src/pkg/runtime/rdebug.goc
index e5b57c481..042b30ace 100644
--- a/src/pkg/runtime/rdebug.goc
+++ b/src/pkg/runtime/rdebug.goc
@@ -20,3 +20,8 @@ func setGCPercent(in int) (out int) {
func setMaxThreads(in int) (out int) {
out = runtime·setmaxthreads(in);
}
+
+func SetPanicOnFault(enabled bool) (old bool) {
+ old = g->paniconfault;
+ g->paniconfault = enabled;
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 3d5b6007a..8d07294e8 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -273,6 +273,7 @@ struct G
bool issystem; // do not output in stack dump
bool isbackground; // ignore in deadlock detector
bool preempt; // preemption signal, duplicates stackguard0 = StackPreempt
+ bool paniconfault; // panic (instead of crash) on unexpected fault address
int8 raceignore; // ignore race detection events
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
diff --git a/src/pkg/runtime/runtime_test.go b/src/pkg/runtime/runtime_test.go
index c67327562..83489480d 100644
--- a/src/pkg/runtime/runtime_test.go
+++ b/src/pkg/runtime/runtime_test.go
@@ -10,9 +10,11 @@ import (
"os"
"os/exec"
. "runtime"
+ "runtime/debug"
"strconv"
"strings"
"testing"
+ "unsafe"
)
var errf error
@@ -131,3 +133,19 @@ func TestRuntimeGogoBytes(t *testing.T) {
func TestStopCPUProfilingWithProfilerOff(t *testing.T) {
SetCPUProfileRate(0)
}
+
+func TestSetPanicOnFault(t *testing.T) {
+ old := debug.SetPanicOnFault(true)
+ defer debug.SetPanicOnFault(old)
+
+ defer func() {
+ if err := recover(); err == nil {
+ t.Fatalf("did not find error in recover")
+ }
+ }()
+
+ var p *int
+ p = (*int)(unsafe.Pointer(^uintptr(0)))
+ println(*p)
+ t.Fatalf("still here - should have faulted")
+}