Files
swift-mirror/test/SILGen/objc_actor.swift
Varun Gandhi 4311a03fbe [AST] Treat actors inheriting from NSObject as SwiftNativeNSObjects.
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.
2021-08-08 00:34:07 -07:00

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