-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Closed
Labels
Milestone
Description
When scanobject looks up the span for a pointer it read from the object it's scanning, it's possible for it to get incorrect information for the span, causing it to ignore the pointer, or, much worse, attempt to mark uninitialized memory.
- scanobject loads a pointer slot out of the object it's scanning, and this happens to be one of the special pointers from the heap into a stack. Call the pointer p and suppose it points into X's stack.
- X, running on another thread, grows its stack and frees its old stack.
- The old stack happens to be large or was the last stack in its span, so X frees this span, setting it to state _MSpanFree.
- scanobject calls heapBitsForObject, which loads the span containing p, which is now in state _MSpanFree, so it ignore it. There's currently a disabled test here that would panic (issue runtime: reenable bad pointer check in GC #10591).
or
- The span gets reused as a heap span.
- scanobject calls heapBitsForObject, which loads the span containing p, which is now in state _MSpanInUse, but doesn't necessarily have an object at p. The not-object at p gets marked, and at this point all sorts of things can go wrong.
This is quite easy to reproduce with a well-placed sleep:
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 146ffbf..fe36f3a 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -190,6 +190,9 @@ func heapBitsForObject(p uintptr) (base uintptr, hbits heapBits, s *mspan) {
// Consult the span table to find the block beginning.
k := p >> _PageShift
s = h_spans[idx]
+ if s != nil && s.state == _MSpanStack {
+ usleep(100)
+ }
if s == nil || pageID(k) < s.start || p >= s.limit || s.state != mSpanInUse {
if s == nil || s.state == _MSpanStack {
// If s is nil, the virtual address has never been part of the heap.
@@ -201,7 +204,7 @@ 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 false {
+ if true {
// Still happens sometimes. We don't know why.
printlock()
print("runtime:objectstart Span weird: p=", hex(p), " k=", hex(k))
With this sleep, GOGC=10 ./runtime.test -test.short
crashes reliably for me.