Currently the checkfinalizers test (TestDetectCleanupOrFinalizerLeak)
only *tries* to ensure the tiny alloc with a cleanup attached shares a
block with other objects. However, what it does is insufficient, because
it could get unlucky and have the last object allocated be the first
object of a new block.
This change changes the test to guarantee that a tiny object is not at
the start of a fresh block by looking at the alignment of the object's
pointer. If the object's pointer is odd, then that's good enough to know
that it shares a block with something else, since the blocks themselves
are aligned to a much higher power of two.
This fixes a failure I've seen on the builders.
Fixes #73810.
Change-Id: Ieafdbb9cccb0d2dc3659a9a5d9d9233718461635
Reviewed-on: https://go-review.googlesource.com/c/go/+/674655
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
}
// N.B. elemsize == 0 indicates a tiny allocation, since no new slot was
- // allocated to fulfill this call to mallocgc.
+ // allocated to fulfill this call to mallocgc. This means checkfinalizer
+ // will only flag an error if there is actually any risk. If an allocation
+ // has the tiny block to itself, it will not get flagged, because we won't
+ // mark the block as a tiny block.
if debug.checkfinalizers != 0 && elemsize == 0 {
setTinyBlockContext(unsafe.Pointer(alignDown(uintptr(x), maxTinySize)))
}
import (
"runtime"
"runtime/debug"
+ "unsafe"
)
func init() {
// Ensure we create an allocation into a tiny block that shares space among several values.
var ctLeak *tiny
- for i := 0; i < 18; i++ {
+ for {
tinySink = ctLeak
ctLeak = new(tiny)
- *ctLeak = tiny(i)
+ *ctLeak = tiny(55)
+ // Make sure the address is an odd value. This is sufficient to
+ // be certain that we're sharing a block with another value and
+ // trip the detector.
+ if uintptr(unsafe.Pointer(ctLeak))%2 != 0 {
+ break
+ }
}
runtime.AddCleanup(ctLeak, func(_ struct{}) {}, struct{}{})