mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
internal/weak: shade pointer in weak-to-strong conversion
There's a bug in the weak-to-strong conversion in that creating the *only* strong pointer to some weakly-held object during the mark phase may result in that object not being properly marked. The exact mechanism for this is that the new strong pointer will always point to a white object (because it was only weakly referenced up until this point) and it can then be stored in a blackened stack, hiding it from the garbage collector. This "hide a white pointer in the stack" problem is pretty much exactly what the Yuasa part of the hybrid write barrier is trying to catch, so we need to do the same thing the write barrier would do: shade the pointer. Added a test and confirmed that it fails with high probability if the pointer shading is missing. Fixes #69210. Change-Id: Iaae64ae95ea7e975c2f2c3d4d1960e74e1bd1c3f Reviewed-on: https://go-review.googlesource.com/c/go/+/610396 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
1b4cf43e42
commit
79fd633632
2 changed files with 108 additions and 1 deletions
|
|
@ -2073,7 +2073,22 @@ func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
|||
// Even if we just swept some random span that doesn't contain this object, because
|
||||
// this object is long dead and its memory has since been reused, we'll just observe nil.
|
||||
ptr := unsafe.Pointer(handle.Load())
|
||||
|
||||
// This is responsible for maintaining the same GC-related
|
||||
// invariants as the Yuasa part of the write barrier. During
|
||||
// the mark phase, it's possible that we just created the only
|
||||
// valid pointer to the object pointed to by ptr. If it's only
|
||||
// ever referenced from our stack, and our stack is blackened
|
||||
// already, we could fail to mark it. So, mark it now.
|
||||
if gcphase != _GCoff {
|
||||
shade(uintptr(ptr))
|
||||
}
|
||||
releasem(mp)
|
||||
|
||||
// Explicitly keep ptr alive. This seems unnecessary since we return ptr,
|
||||
// but let's be explicit since it's important we keep ptr alive across the
|
||||
// call to shade.
|
||||
KeepAlive(ptr)
|
||||
return ptr
|
||||
}
|
||||
|
||||
|
|
@ -2081,6 +2096,9 @@ func internal_weak_runtime_makeStrongFromWeak(u unsafe.Pointer) unsafe.Pointer {
|
|||
func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
||||
// First try to retrieve without allocating.
|
||||
if handle := getWeakHandle(p); handle != nil {
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return handle
|
||||
}
|
||||
|
||||
|
|
@ -2105,6 +2123,10 @@ func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
|||
scanblock(uintptr(unsafe.Pointer(&s.handle)), goarch.PtrSize, &oneptrmask[0], gcw, nil)
|
||||
releasem(mp)
|
||||
}
|
||||
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return s.handle
|
||||
}
|
||||
|
||||
|
|
@ -2124,7 +2146,7 @@ func getOrAddWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
|||
}
|
||||
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to this.
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return handle
|
||||
}
|
||||
|
|
@ -2154,6 +2176,9 @@ func getWeakHandle(p unsafe.Pointer) *atomic.Uintptr {
|
|||
unlock(&span.speciallock)
|
||||
releasem(mp)
|
||||
|
||||
// Keep p alive for the duration of the function to ensure
|
||||
// that it cannot die while we're trying to do this.
|
||||
KeepAlive(p)
|
||||
return handle
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue