blob: ea352a4e3e92f1b889a371566561293672a0447c [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"internal/asan"
"internal/race"
"runtime"
"runtime/debug"
"unsafe"
)
func init() {
register("DetectFinalizerAndCleanupLeaks", DetectFinalizerAndCleanupLeaks)
}
type tiny uint8
var tinySink *tiny
// Intended to be run only with `GODEBUG=checkfinalizers=1`.
func DetectFinalizerAndCleanupLeaks() {
type T *int
defer debug.SetGCPercent(debug.SetGCPercent(-1))
// Leak a cleanup.
cLeak := new(T)
runtime.AddCleanup(cLeak, func(x int) {
**cLeak = x
}, int(0))
// Have a regular cleanup to make sure it doesn't trip the detector.
cNoLeak := new(T)
runtime.AddCleanup(cNoLeak, func(_ int) {}, int(0))
// Add a cleanup that only temporarily leaks cNoLeak.
runtime.AddCleanup(cNoLeak, func(x int) {
**cNoLeak = x
}, int(0)).Stop()
if !asan.Enabled && !race.Enabled {
// Ensure we create an allocation into a tiny block that shares space among several values.
//
// Don't do this with ASAN and in race mode, where the tiny allocator is disabled.
// We might just loop forever here in that case.
var ctLeak *tiny
for {
tinySink = ctLeak
ctLeak = new(tiny)
*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{}{})
}
// Leak a finalizer.
fLeak := new(T)
runtime.SetFinalizer(fLeak, func(_ *T) {
**fLeak = 12
})
// Have a regular finalizer to make sure it doesn't trip the detector.
fNoLeak := new(T)
runtime.SetFinalizer(fNoLeak, func(x *T) {
**x = 51
})
// runtime.GC here should crash.
runtime.GC()
println("OK")
}