]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: guarantee checkfinalizers test allocates in a shared tiny block
authorMichael Anthony Knyszek <mknyszek@google.com>
Tue, 20 May 2025 20:26:56 +0000 (20:26 +0000)
committerMichael Knyszek <mknyszek@google.com>
Wed, 21 May 2025 16:50:31 +0000 (09:50 -0700)
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>
src/runtime/malloc.go
src/runtime/testdata/testprog/checkfinalizers.go

index 25caf0625beed58226249cf68f011908ac25ce8c..abd94fda08e4dd21c0c914a47d90f0d73be0a33e 100644 (file)
@@ -1672,7 +1672,10 @@ func postMallocgcDebug(x unsafe.Pointer, elemsize uintptr, typ *_type) {
        }
 
        // 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)))
        }
index b542f575fe34e6e018c5e86a878bcc9b648a08f4..a2fe104462d6b789104224155147377c12830fca 100644 (file)
@@ -7,6 +7,7 @@ package main
 import (
        "runtime"
        "runtime/debug"
+       "unsafe"
 )
 
 func init() {
@@ -40,10 +41,16 @@ func DetectFinalizerAndCleanupLeaks() {
 
        // 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{}{})