Skip to content

[AST] Treat actors inheriting from NSObject as SwiftNativeNSObjects. #38803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

varungandhi-apple
Copy link
Contributor

@varungandhi-apple varungandhi-apple commented Aug 8, 2021

Previously, omitting @objc meant that ObjC-refcounting was used (even if : NSObject
was present), meaning that the multi-step deinitialization of different zombie states was
bypassed, leading to eager deallocation on the last release and user-after-free.

Fixes rdar://80863853.

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.
@varungandhi-apple
Copy link
Contributor Author

I wasn't entirely sure if the best place to test this was at the SIL level, or LLVM IR, or if I should add a runtime test too to make sure the superclass is indeed SwiftNativeNSObject. Happy to add more tests.

@varungandhi-apple
Copy link
Contributor Author

@swift-ci smoke test

@varungandhi-apple varungandhi-apple merged commit 7bbc29a into swiftlang:main Aug 9, 2021
@varungandhi-apple varungandhi-apple deleted the vg-objc-actors-took-2-weeks-of-my-life branch August 9, 2021 17:54
@rjmccall
Copy link
Contributor

rjmccall commented Aug 9, 2021

LGTM; this should have been kept in the condition when Doug introduced the @objc tweak.

@DougGregor
Copy link
Member

LGTM; this should have been kept in the condition when Doug introduced the @objc tweak.

Seems like it should be an assertion that any default actor uses Swift reference counting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants