mirror of
https://github.com/golang/go
synced 2024-11-18 00:54:45 -07:00
runtime: improve invalid pointer error message
By default, the runtime panics if it detects a pointer to an unallocated span. At this point, this usually catches bad uses of unsafe or cgo in user code (though it could also catch runtime bugs). Unfortunately, the rather cryptic error misleads users, offers users little help with debugging their own problem, and offers the Go developers little help with root-causing. Improve the error message in various ways. First, the wording is improved to make it clearer what condition was detected and to suggest that this may be the result of incorrect use of unsafe or cgo. Second, we add a dump of the object containing the bad pointer so that there's at least some hope of figuring out why a bad pointer was stored in the Go heap. Change-Id: I57b91b12bc3cb04476399d7706679e096ce594b9 Reviewed-on: https://go-review.googlesource.com/14763 Reviewed-by: Rick Hudson <rlh@golang.org>
This commit is contained in:
parent
7360638da0
commit
b7c55ba496
@ -587,3 +587,40 @@ func main() {
|
||||
fmt.Println("done")
|
||||
}
|
||||
`
|
||||
|
||||
func TestInvalidptrCrash(t *testing.T) {
|
||||
output := executeTest(t, invalidptrCrashSource, nil)
|
||||
// Check that the bad pointer was detected.
|
||||
want1 := "found bad pointer in Go heap"
|
||||
if !strings.Contains(output, want1) {
|
||||
t.Fatalf("failed to detect bad pointer; output does not contain %q:\n%s", want1, output)
|
||||
}
|
||||
// Check that we dumped the object containing the bad pointer.
|
||||
want2 := "*(object+0) = 0x12345678"
|
||||
if !strings.Contains(output, want2) {
|
||||
t.Fatalf("failed to dump source object; output does not contain %q:\n%s", want2, output)
|
||||
}
|
||||
}
|
||||
|
||||
const invalidptrCrashSource = `
|
||||
package main
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
var x = new(struct {
|
||||
magic uintptr
|
||||
y *byte
|
||||
})
|
||||
func main() {
|
||||
runtime.GC()
|
||||
x.magic = 0x12345678
|
||||
x.y = &make([]byte, 64*1024)[0]
|
||||
weasel := uintptr(unsafe.Pointer(x.y))
|
||||
x.y = nil
|
||||
runtime.GC()
|
||||
x.y = (*byte)(unsafe.Pointer(weasel))
|
||||
runtime.GC()
|
||||
println("failed to detect bad pointer")
|
||||
}
|
||||
`
|
||||
|
@ -182,7 +182,11 @@ func heapBitsForSpan(base uintptr) (hbits heapBits) {
|
||||
// If p does not point into a heap object,
|
||||
// return base == 0
|
||||
// otherwise return the base of the object.
|
||||
func heapBitsForObject(p uintptr) (base uintptr, hbits heapBits, s *mspan) {
|
||||
//
|
||||
// refBase and refOff optionally give the base address of the object
|
||||
// in which the pointer p was found and the byte offset at which it
|
||||
// was found. These are used for error reporting.
|
||||
func heapBitsForObject(p, refBase, refOff uintptr) (base uintptr, hbits heapBits, s *mspan) {
|
||||
arenaStart := mheap_.arena_start
|
||||
if p < arenaStart || p >= mheap_.arena_used {
|
||||
return
|
||||
@ -203,18 +207,28 @@ func heapBitsForObject(p uintptr) (base uintptr, hbits heapBits, s *mspan) {
|
||||
|
||||
// The following ensures that we are rigorous about what data
|
||||
// structures hold valid pointers.
|
||||
// TODO(rsc): Check if this still happens.
|
||||
if debug.invalidptr != 0 {
|
||||
// Still happens sometimes. We don't know why.
|
||||
// Typically this indicates an incorrect use
|
||||
// of unsafe or cgo to store a bad pointer in
|
||||
// the Go heap. It may also indicate a runtime
|
||||
// bug.
|
||||
//
|
||||
// TODO(austin): We could be more aggressive
|
||||
// and detect pointers to unallocated objects
|
||||
// in allocated spans.
|
||||
printlock()
|
||||
print("runtime:objectstart Span weird: p=", hex(p), " k=", hex(k))
|
||||
if s == nil {
|
||||
print(" s=nil\n")
|
||||
print("runtime: pointer ", hex(p))
|
||||
if s.state != mSpanInUse {
|
||||
print(" to unallocated span")
|
||||
} else {
|
||||
print(" s.start=", hex(s.start<<_PageShift), " s.limit=", hex(s.limit), " s.state=", s.state, "\n")
|
||||
print(" to unused region of span")
|
||||
}
|
||||
printunlock()
|
||||
throw("objectstart: bad pointer in unexpected span")
|
||||
print("idx=", hex(idx), " span.start=", hex(s.start<<_PageShift), " span.limit=", hex(s.limit), " span.state=", s.state, "\n")
|
||||
if refBase != 0 {
|
||||
print("runtime: found in object at *(", hex(refBase), "+", hex(off), ")\n")
|
||||
gcDumpObject("object", refBase, refOff)
|
||||
}
|
||||
throw("found bad pointer in Go heap (incorrect use of unsafe or cgo?)")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -659,7 +659,7 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
|
||||
// Same work as in scanobject; see comments there.
|
||||
obj := *(*uintptr)(unsafe.Pointer(b + i))
|
||||
if obj != 0 && arena_start <= obj && obj < arena_used {
|
||||
if obj, hbits, span := heapBitsForObject(obj); obj != 0 {
|
||||
if obj, hbits, span := heapBitsForObject(obj, b, i); obj != 0 {
|
||||
greyobject(obj, b, i, hbits, span, gcw)
|
||||
}
|
||||
}
|
||||
@ -725,7 +725,7 @@ func scanobject(b uintptr, gcw *gcWork) {
|
||||
// Check if it points into heap and not back at the current object.
|
||||
if obj != 0 && arena_start <= obj && obj < arena_used && obj-b >= n {
|
||||
// Mark the object.
|
||||
if obj, hbits, span := heapBitsForObject(obj); obj != 0 {
|
||||
if obj, hbits, span := heapBitsForObject(obj, b, i); obj != 0 {
|
||||
greyobject(obj, b, i, hbits, span, gcw)
|
||||
}
|
||||
}
|
||||
@ -739,7 +739,7 @@ func scanobject(b uintptr, gcw *gcWork) {
|
||||
// Preemption must be disabled.
|
||||
//go:nowritebarrier
|
||||
func shade(b uintptr) {
|
||||
if obj, hbits, span := heapBitsForObject(b); obj != 0 {
|
||||
if obj, hbits, span := heapBitsForObject(b, 0, 0); obj != 0 {
|
||||
gcw := &getg().m.p.ptr().gcw
|
||||
greyobject(obj, 0, 0, hbits, span, gcw)
|
||||
if gcphase == _GCmarktermination || gcBlackenPromptly {
|
||||
@ -810,7 +810,7 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork
|
||||
// field at byte offset off in obj.
|
||||
func gcDumpObject(label string, obj, off uintptr) {
|
||||
if obj < mheap_.arena_start || obj >= mheap_.arena_used {
|
||||
print(label, "=", hex(obj), " is not a heap object\n")
|
||||
print(label, "=", hex(obj), " is not in the Go heap\n")
|
||||
return
|
||||
}
|
||||
k := obj >> _PageShift
|
||||
|
Loading…
Reference in New Issue
Block a user