mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Previously, there was a divergence in behavior between marking an actor as: - objc: This would lead to usage of Swift reference counting. - subclass of NSObject but not objc: This would lead to usage of ObjC reference counting. By itself, this might seem OK, after all, the two reference counting schemes are compatible (you can pass things from one language to another). However, there is a difference here because actors have a multi-step deinitialization process due to the way the reference accounting works with tasks. When the last reference is released, the destroy() method is called which only puts the actor into a Zombie_Latching state and deallocation happens later on. In contrast, when the ObjC runtime releases the last reference, it immediately deallocates the actor, without calling the destroy() method or having any awareness about actors. This mismatch leads to a use-after-free in the Swift runtime; normally, the actor would be in the Zombie_Latching state and when the final running task finishes, it would end up deallocating the actor. This expectation is not met when the memory is freed by the ObjC runtime, hence the UAF. Normally, I would find it a bit odd to see a commit come with acknowledgements but in this case I think maybe it's okay, given that this took me 2+ weeks to investigate and fix. Thanks to: - Dani C. in helping reproduce the issue with a large and small test case. - John for answering questions about the actor and job lifecycle. - Mike Ash for providing generous help with debugging. Fixes rdar://80863853.
35 lines
966 B
Swift
35 lines
966 B
Swift
// RUN: %target-swift-frontend -emit-silgen %s -swift-version 5 -disable-availability-checking | %FileCheck %s
|
|
// REQUIRES: concurrency
|
|
// REQUIRES: objc_interop
|
|
|
|
// rdar://80863853 - For an actor inheriting from NSObject and using '@objc'
|
|
// should have the same effect: the effective superclass is SwiftNativeNSObject
|
|
// (see 945011d39f8b271b8906bd509aac3aa954f4fc57) not NSObject.
|
|
// Check that we don't treat any case as an ObjC class.
|
|
|
|
import Foundation
|
|
|
|
public actor MyClass1: NSObject {
|
|
public var x: Int
|
|
public init(_ x: Int) { self.x = x }
|
|
}
|
|
|
|
// CHECK: alloc_ref $MyClass1
|
|
// CHECK-NOT: alloc_ref [objc] $MyClass1
|
|
|
|
@objc public actor MyClass2 {
|
|
public var x: Int
|
|
public init(_ x: Int) { self.x = x }
|
|
}
|
|
|
|
// CHECK: alloc_ref $MyClass2
|
|
// CHECK-NOT: alloc_ref [objc] $MyClass2
|
|
|
|
@objc public actor MyClass3: NSObject {
|
|
public var x: Int
|
|
public init(_ x: Int) { self.x = x }
|
|
}
|
|
|
|
// CHECK: alloc_ref $MyClass3
|
|
// CHECK-NOT: alloc_ref [objc] $MyClass3
|