// 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")
}
*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.
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()
}
}
+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.
//