Skip to content

Commit d91d076

Browse files
committed
runtime/debug: provide Addr method for errors from SetPanicOnFault
When we're building a panic that's triggered by a memory fault when SetPanicOnFault has been called, include an Addr method. This method reports the address at which the fault occurred. Fixes #37023 RELNOTE=yes Change-Id: Idff144587d6b75070fdc861a36efec76f4ec7384 Reviewed-on: https://go-review.googlesource.com/c/go/+/249677 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Keith Randall <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 65dfe4a commit d91d076

File tree

7 files changed

+86
-4
lines changed

7 files changed

+86
-4
lines changed

src/runtime/debug/garbage.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ func SetMaxThreads(threads int) int {
139139
// manipulation of memory may cause faults at non-nil addresses in less
140140
// dramatic situations; SetPanicOnFault allows such programs to request
141141
// that the runtime trigger only a panic, not a crash.
142+
// The runtime.Error that the runtime panics with may have an additional method:
143+
// Addr() uintptr
144+
// If that method exists, it returns the memory address which triggered the fault.
145+
// The results of Addr are best-effort and the veracity of the result
146+
// may depend on the platform.
142147
// SetPanicOnFault applies only to the current goroutine.
143148
// It returns the previous setting.
144149
func SetPanicOnFault(enabled bool) bool {

src/runtime/debug/panic_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build aix darwin dragonfly freebsd linux netbsd openbsd
6+
7+
// TODO: test on Windows?
8+
9+
package debug_test
10+
11+
import (
12+
"runtime/debug"
13+
"syscall"
14+
"testing"
15+
"unsafe"
16+
)
17+
18+
func TestPanicOnFault(t *testing.T) {
19+
m, err := syscall.Mmap(-1, 0, 0x1000, syscall.PROT_READ /* Note: no PROT_WRITE */, syscall.MAP_SHARED|syscall.MAP_ANON)
20+
if err != nil {
21+
t.Fatalf("can't map anonymous memory: %s", err)
22+
}
23+
defer syscall.Munmap(m)
24+
old := debug.SetPanicOnFault(true)
25+
defer debug.SetPanicOnFault(old)
26+
const lowBits = 0x3e7
27+
defer func() {
28+
r := recover()
29+
if r == nil {
30+
t.Fatalf("write did not fault")
31+
}
32+
type addressable interface {
33+
Addr() uintptr
34+
}
35+
a, ok := r.(addressable)
36+
if !ok {
37+
t.Fatalf("fault does not contain address")
38+
}
39+
want := uintptr(unsafe.Pointer(&m[lowBits]))
40+
got := a.Addr()
41+
if got != want {
42+
t.Fatalf("fault address %x, want %x", got, want)
43+
}
44+
}()
45+
m[lowBits] = 1 // will fault
46+
}

src/runtime/error.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ func (e errorString) Error() string {
7777
return "runtime error: " + string(e)
7878
}
7979

80+
type errorAddressString struct {
81+
msg string // error message
82+
addr uintptr // memory address where the error occurred
83+
}
84+
85+
func (e errorAddressString) RuntimeError() {}
86+
87+
func (e errorAddressString) Error() string {
88+
return "runtime error: " + e.msg
89+
}
90+
91+
// Addr returns the memory address where a fault occurred.
92+
// The address provided is best-effort.
93+
// The veracity of the result may depend on the platform.
94+
// Errors providing this method will only be returned as
95+
// a result of using runtime/debug.SetPanicOnFault.
96+
func (e errorAddressString) Addr() uintptr {
97+
return e.addr
98+
}
99+
80100
// plainError represents a runtime error described a string without
81101
// the prefix "runtime error: " after invoking errorString.Error().
82102
// See Issue #14965.

src/runtime/os_plan9.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,12 @@ func sigpanic() {
9292
}
9393
addr := note[i:]
9494
g.sigcode1 = uintptr(atolwhex(addr))
95-
if g.sigcode1 < 0x1000 || g.paniconfault {
95+
if g.sigcode1 < 0x1000 {
9696
panicmem()
9797
}
98+
if g.paniconfault {
99+
panicmemAddr(g.sigcode1)
100+
}
98101
print("unexpected fault address ", hex(g.sigcode1), "\n")
99102
throw("fault")
100103
case _SIGTRAP:

src/runtime/panic.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ func panicmem() {
212212
panic(memoryError)
213213
}
214214

215+
func panicmemAddr(addr uintptr) {
216+
panicCheck2("invalid memory address or nil pointer dereference")
217+
panic(errorAddressString{msg: "invalid memory address or nil pointer dereference", addr: addr})
218+
}
219+
215220
// Create a new deferred function fn with siz bytes of arguments.
216221
// The compiler turns a defer statement into a call to this.
217222
//go:nosplit

src/runtime/signal_unix.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ func sigpanic() {
710710
}
711711
// Support runtime/debug.SetPanicOnFault.
712712
if g.paniconfault {
713-
panicmem()
713+
panicmemAddr(g.sigcode1)
714714
}
715715
print("unexpected fault address ", hex(g.sigcode1), "\n")
716716
throw("fault")
@@ -720,7 +720,7 @@ func sigpanic() {
720720
}
721721
// Support runtime/debug.SetPanicOnFault.
722722
if g.paniconfault {
723-
panicmem()
723+
panicmemAddr(g.sigcode1)
724724
}
725725
print("unexpected fault address ", hex(g.sigcode1), "\n")
726726
throw("fault")

src/runtime/signal_windows.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,12 @@ func sigpanic() {
242242

243243
switch g.sig {
244244
case _EXCEPTION_ACCESS_VIOLATION:
245-
if g.sigcode1 < 0x1000 || g.paniconfault {
245+
if g.sigcode1 < 0x1000 {
246246
panicmem()
247247
}
248+
if g.paniconfault {
249+
panicmemAddr(g.sigcode1)
250+
}
248251
print("unexpected fault address ", hex(g.sigcode1), "\n")
249252
throw("fault")
250253
case _EXCEPTION_INT_DIVIDE_BY_ZERO:

0 commit comments

Comments
 (0)