diff options
author | Russ Cox <rsc@golang.org> | 2014-02-20 16:18:05 -0500 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2014-02-20 16:18:05 -0500 |
commit | ed591404c19dffaf84876d8e3d75db926b78a130 (patch) | |
tree | bd9764edb7953c1b09a4ac8231c994e370947744 | |
parent | 50be4ad5a71a5db95c620805be653d41c7d41ebd (diff) | |
download | go-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.txt | 1 | ||||
-rw-r--r-- | src/pkg/runtime/debug/garbage.go | 11 | ||||
-rw-r--r-- | src/pkg/runtime/os_darwin.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_dragonfly.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_freebsd.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_linux.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_netbsd.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_openbsd.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_solaris.c | 4 | ||||
-rw-r--r-- | src/pkg/runtime/os_windows.c | 2 | ||||
-rw-r--r-- | src/pkg/runtime/proc.c | 1 | ||||
-rw-r--r-- | src/pkg/runtime/rdebug.goc | 5 | ||||
-rw-r--r-- | src/pkg/runtime/runtime.h | 1 | ||||
-rw-r--r-- | src/pkg/runtime/runtime_test.go | 18 |
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") +} |