runtime: panic on AddCleanup with self pointer

For #75066

Change-Id: Ifd555586fb448e7510fed16372648bdd7ec0ab4a
Reviewed-on: https://go-review.googlesource.com/c/go/+/697535
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Ian Lance Taylor
2025-08-19 17:00:29 -07:00
committed by Gopher Robot
parent 03f499ec46
commit 06412288cf
2 changed files with 36 additions and 2 deletions

View File

@@ -84,7 +84,8 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup {
// Check that arg is not equal to ptr.
argType := abi.TypeOf(arg)
if kind := argType.Kind(); kind == abi.Pointer || kind == abi.UnsafePointer {
kind := argType.Kind()
if kind == abi.Pointer || kind == abi.UnsafePointer {
if unsafe.Pointer(ptr) == *((*unsafe.Pointer)(unsafe.Pointer(&arg))) {
panic("runtime.AddCleanup: ptr is equal to arg, cleanup will never run")
}
@@ -119,7 +120,7 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup {
*argv = arg
// Find the containing object.
base, _, _ := findObject(usptr, 0, 0)
base, span, _ := findObject(usptr, 0, 0)
if base == 0 {
if isGoPointerWithoutSpan(unsafe.Pointer(ptr)) {
// Cleanup is a noop.
@@ -128,6 +129,22 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup {
panic("runtime.AddCleanup: ptr not in allocated block")
}
// Check that arg is not within ptr.
if kind == abi.Pointer || kind == abi.UnsafePointer {
argPtr := uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&arg)))
if argPtr >= base && argPtr < base+span.elemsize {
// It's possible that both pointers are separate
// parts of a tiny allocation, which is OK.
// We side-stepped the tiny allocator above for
// the allocation for the cleanup,
// but the argument itself can still overlap
// with the value to which the cleanup is attached.
if span.spanclass != tinySpanClass {
panic("runtime.AddCleanup: ptr is within arg, cleanup will never run")
}
}
}
// Create another G if necessary.
if gcCleanups.needG() {
gcCleanups.createGs()

View File

@@ -337,6 +337,23 @@ func TestCleanupLost(t *testing.T) {
}
}
func TestCleanupUnreachable(t *testing.T) {
defer func() {
if recover() == nil {
t.Error("AddCleanup failed to detect self-pointer")
}
}()
type T struct {
p *byte // use *byte to avoid tiny allocator
f int
}
v := &T{}
runtime.AddCleanup(v, func(*int) {
t.Error("cleanup ran unexpectedly")
}, &v.f)
}
// BenchmarkAddCleanupAndStop benchmarks adding and removing a cleanup
// from the same allocation.
//