]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: throw if the runtime panics with out of bounds index
authorIan Lance Taylor <iant@golang.org>
Thu, 28 Jun 2018 23:45:28 +0000 (16:45 -0700)
committerIan Lance Taylor <iant@golang.org>
Fri, 29 Jun 2018 21:29:17 +0000 (21:29 +0000)
If the runtime code panics due to a bad index or slice expression,
then throw instead of panicing. This will skip calls to recover and dump
the entire runtime stack trace. The runtime should never panic due to
an out of bounds index, and this will help with debugging if it does.

For #24991
Updates #25201

Change-Id: I85a9feded8f0de914ee1558425931853223c0514
Reviewed-on: https://go-review.googlesource.com/121515
Reviewed-by: Austin Clements <austin@google.com>
src/runtime/crash_test.go
src/runtime/export_test.go
src/runtime/panic.go

index 843b4150065d901a85f7107af387c06b2a38d2de..9f11aea4e9f5fc12b6860cfe216898e4c2d8dd24 100644 (file)
@@ -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)
+       }
+}
index b21179cc8cc883685f79dd91aea85e21a261e809..7ebdfc15208a0f2ad714e816ff67853cf278d280 100644 (file)
@@ -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]
+}
index ce367cfa70fc02760b68c03123cb7b324f50ff59..7bb7f9b90c9d18e6fc56193e20dcc7fc765efa86 100644 (file)
@@ -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)
 }