Skip to content

Commit 108f780

Browse files
committed
[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
1 parent 67cbe90 commit 108f780

File tree

2 files changed

+34
-0
lines changed

2 files changed

+34
-0
lines changed

stdlib/public/runtime/HeapObject.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,13 @@ void swift::swift_rootObjCDealloc(HeapObject *self) {
710710
void swift::swift_deallocClassInstance(HeapObject *object,
711711
size_t allocatedSize,
712712
size_t allocatedAlignMask) {
713+
size_t retainCount = swift_retainCount(object);
714+
if (SWIFT_UNLIKELY(retainCount > 1))
715+
swift::fatalError(0,
716+
"Object %p deallocated with retain count %zd, reference "
717+
"may have escaped from deinit.\n",
718+
object, retainCount);
719+
713720
#if SWIFT_OBJC_INTEROP
714721
// We need to let the ObjC runtime clean up any associated objects or weak
715722
// references associated with this object.
@@ -718,6 +725,7 @@ void swift::swift_deallocClassInstance(HeapObject *object,
718725
#else
719726
const bool fastDeallocSupported = true;
720727
#endif
728+
721729
if (!fastDeallocSupported || !object->refCounts.getPureSwiftDeallocation()) {
722730
objc_destructInstance((id)object);
723731
}

test/Runtime/deinit_escape.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-run-simple-swift
2+
3+
// REQUIRES: executable_test
4+
// UNSUPPORTED: use_os_stdlib
5+
// UNSUPPORTED: back_deployment_runtime
6+
7+
import StdlibUnittest
8+
9+
var DeinitEscapeTestSuite = TestSuite("DeinitEscape")
10+
11+
var globalObjects: [AnyObject] = []
12+
13+
DeinitEscapeTestSuite.test("deinit escapes self") {
14+
expectCrashLater()
15+
16+
class C {
17+
deinit {
18+
globalObjects.append(self)
19+
}
20+
}
21+
_ = C()
22+
23+
expectUnreachable()
24+
}
25+
26+
runAllTests()

0 commit comments

Comments
 (0)