diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 843b415006..9f11aea4e9 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -654,3 +654,33 @@ func TestAbort(t *testing.T) { } } } + +// For TestRuntimePanic: test a panic in the runtime package without +// involving the testing harness. +func init() { + if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" { + defer func() { + if r := recover(); r != nil { + // We expect to crash, so exit 0 + // to indicate failure. + os.Exit(0) + } + }() + runtime.PanicForTesting(nil, 1) + // We expect to crash, so exit 0 to indicate failure. + os.Exit(0) + } +} + +func TestRuntimePanic(t *testing.T) { + testenv.MustHaveExec(t) + cmd := exec.Command(os.Args[0], "-test.run=TestRuntimePanic") + cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err == nil { + t.Error("child process did not fail") + } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) { + t.Errorf("output did not contain expected string %q", want) + } +} diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index b21179cc8c..7ebdfc1520 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -451,3 +451,13 @@ type G = g func Getg() *G { return getg() } + +//go:noinline +func PanicForTesting(b []byte, i int) byte { + return unexportedPanicForTesting(b, i) +} + +//go:noinline +func unexportedPanicForTesting(b []byte, i int) byte { + return b[i] +} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index ce367cfa70..7bb7f9b90c 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -23,7 +23,23 @@ func panicCheckMalloc(err error) { var indexError = error(errorString("index out of range")) +// The panicindex, panicslice, and panicdivide functions are called by +// code generated by the compiler for out of bounds index expressions, +// out of bounds slice expressions, and division by zero. The +// panicdivide (again), panicoverflow, panicfloat, and panicmem +// functions are called by the signal handler when a signal occurs +// indicating the respective problem. +// +// Since panicindex and panicslice are never called directly, and +// since the runtime package should never have an out of bounds slice +// or array reference, if we see those functions called from the +// runtime package we turn the panic into a throw. That will dump the +// entire runtime stack for easier debugging. + func panicindex() { + if hasprefix(funcname(findfunc(getcallerpc())), "runtime.") { + throw(string(indexError.(errorString))) + } panicCheckMalloc(indexError) panic(indexError) } @@ -31,6 +47,9 @@ func panicindex() { var sliceError = error(errorString("slice bounds out of range")) func panicslice() { + if hasprefix(funcname(findfunc(getcallerpc())), "runtime.") { + throw(string(sliceError.(errorString))) + } panicCheckMalloc(sliceError) panic(sliceError) }