From 108f7805e5780c9654b66c3a33f8f72da2bb2ccd Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Fri, 18 Nov 2022 15:52:50 -0500 Subject: [PATCH] [Runtime] Fatal error if self escapes from deinit. When deallocating a class instance, we check the retain count of the instance and error if it's greater than 1. Self is allowed to be temporarily passed to other code from deinit, but there's no way to extend the lifetime of the object. Retaining it no longer extensd the lifetime. If self escapes from deinit, the result is a dangling pointer and eventual crash. Instead of crashing randomly due to a dangling pointer, crash deliberately when destroying an object that has escaped. rdar://93848484 --- stdlib/public/runtime/HeapObject.cpp | 8 ++++++++ test/Runtime/deinit_escape.swift | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/Runtime/deinit_escape.swift diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index 9b3b2b81c989e..4421ae69eed43 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -710,6 +710,13 @@ void swift::swift_rootObjCDealloc(HeapObject *self) { void swift::swift_deallocClassInstance(HeapObject *object, size_t allocatedSize, size_t allocatedAlignMask) { + size_t retainCount = swift_retainCount(object); + if (SWIFT_UNLIKELY(retainCount > 1)) + swift::fatalError(0, + "Object %p deallocated with retain count %zd, reference " + "may have escaped from deinit.\n", + object, retainCount); + #if SWIFT_OBJC_INTEROP // We need to let the ObjC runtime clean up any associated objects or weak // references associated with this object. @@ -718,6 +725,7 @@ void swift::swift_deallocClassInstance(HeapObject *object, #else const bool fastDeallocSupported = true; #endif + if (!fastDeallocSupported || !object->refCounts.getPureSwiftDeallocation()) { objc_destructInstance((id)object); } diff --git a/test/Runtime/deinit_escape.swift b/test/Runtime/deinit_escape.swift new file mode 100644 index 0000000000000..31593b3addb87 --- /dev/null +++ b/test/Runtime/deinit_escape.swift @@ -0,0 +1,26 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import StdlibUnittest + +var DeinitEscapeTestSuite = TestSuite("DeinitEscape") + +var globalObjects: [AnyObject] = [] + +DeinitEscapeTestSuite.test("deinit escapes self") { + expectCrashLater() + + class C { + deinit { + globalObjects.append(self) + } + } + _ = C() + + expectUnreachable() +} + +runAllTests()