Xcode 16.3: UnsafeContinuation.resume EXC_BAD_ACCESS crash

I started seeing an odd crash when compiling with Xcode 16.3/ Swift 6.1 and in Swift 6 language mode (still reproducible with Xcode 16.4 Beta 1).

I don't have a dedicated small reliable repro, but in my setup, the following view:

struct ContentView: View {
  var body: some View {
    Text("")
      .task {
        let service: API = Impl()
        await service.foo() ← crash
      }
  }
}

Where the service is defined in a separate package (which may or may not have been compiled in Swift 6 mode):

@objc
protocol API {
  func foo() async
}

class Impl: API {
  func foo() async {}
}

Leads to a very frequent (~ 90% of the time) instant crash:

Thread 6 Queue : com.apple.root.user-initiated-qos.cooperative (concurrent)
#0	0x00000002495bcc48 in resumeTaskAfterContinuation ()
#1	0x00000002495bb8f8 in swift_continuation_resumeImpl ()
#2	0x0000000102310f4c in UnsafeContinuation.resume<>(returning:) ()
#3	0x000000010230fd6c in _resumeUnsafeContinuation<()>(_:_:) ()
#4	0x0000000103806090 in @objc completion handler block implementation for @escaping @callee_unowned @convention(block) () -> () with result type () ()
#5	0x0000000103e7e5a4 in @objc closure #1 in Impl.foo() ()
#6	0x0000000103e7e6ac in partial apply for @objc closure #1 in Impl.foo() ()
#7	0x00000001022f7448 in thunk for @escaping @callee_guaranteed @Sendable @async () -> () ()
#8	0x00000001022f75a4 in partial apply for thunk for @escaping @callee_guaranteed @Sendable @async () -> () ()
#9	0x00000001022f7680 in thunk for @escaping @isolated(any) @callee_guaranteed @async () -> () ()
#10	0x00000001022f77e4 in partial apply for thunk for @escaping @isolated(any) @callee_guaranteed @async () -> () ()
#11	0x00000001022f7acc in specialized thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()
#12	0x00000001022f7c14 in partial apply for specialized thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()
0x2495bcc1c <+96>:  cmn    x8, #0x1
    0x2495bcc20 <+100>: b.ne   0x2495bcca0               ; <+228>
    0x2495bcc24 <+104>: adrp   x0, 33026
    0x2495bcc28 <+108>: add    x0, x0, #0xcc8            ; continuationChecking::ActiveContinuations
    0x2495bcc2c <+112>: add    x1, sp, #0x8
    0x2495bcc30 <+116>: bl     0x2495bcce4               ; unsigned long std::__1::__hash_table<swift::ContinuationAsyncContext*, std::__1::hash<swift::ContinuationAsyncContext*>, std::__1::equal_to<swift::ContinuationAsyncContext*>, std::__1::allocator<swift::ContinuationAsyncContext*>>::__erase_unique<swift::ContinuationAsyncContext*>(swift::ContinuationAsyncContext* const&)
    0x2495bcc34 <+120>: cbz    x0, 0x2495bccbc           ; <+256>
    0x2495bcc38 <+124>: adrp   x0, 33026
    0x2495bcc3c <+128>: add    x0, x0, #0xcc0            ; continuationChecking::ActiveContinuationsLock
    0x2495bcc40 <+132>: bl     0x2495c32fc               ; symbol stub for: os_unfair_lock_unlock
    0x2495bcc44 <+136>: add    x8, x20, #0x18
->  0x2495bcc48 <+140>: ldapr  x21, [x8] ← crash

It is definitely related to the @objc annotation. The crash doesn't occur if I point to the implementation directly (or via some API), or if I remove @objc.

Does anyone have any thoughts/ suggestions?

Thanks!

Is this perhaps the same as @objc func foo() async? ? It had been labeled as „works as intended“ (aka we don’t care).

The corresponding [SR-15291] Concurrency: Crash when invoking async @objc function · Issue #57613 · swiftlang/swift · GitHub is also still open :man_shrugging:t3:

have you tried building with thread sanitizer and/or address sanitizer (docs) enabled to see if that helps narrow down where things might be going wrong more specifically?

That was specific to invoking it by passing a selector to NSNotificationCenter. In the general case it could work, but NSNotificationCenter doesn't call asynchronous methods correctly.

Thanks for the suggestion! TSan didn't output anything near the crash, but ASan did:

thread info -s

thread #3: tid = 0x89889, 0x000000011e47361c libclang_rt.asan_iossim_dynamic.dylib`__asan::AsanDie(), queue = 'com.apple.root.user-initiated-qos.cooperative', stop reason = Use of deallocated memory
{
  "access_size": 8,
  "access_type": 0,
  "address": 4852352336,
  "bp": 6156807184,
  "description": "heap-use-after-free",
  "instrumentation_class": "AddressSanitizer",
  "pc": 4316186644,
  "sp": 6156807176,
  "stop_type": "fatal_error"
}
Stdout
=================================================================
e[1me[31m==33554==ERROR: AddressSanitizer: heap-use-after-free on address 0x000121390550 at pc 0x00010143c814 bp 0x00016ef96c10 sp 0x00016ef96c08
e[1me[0me[1me[34mREAD of size 8 at 0x000121390550 thread T2e[1me[0m
    #0 0x00010143c810 in UnsafeContinuation.resume<>(returning:)+0x48 (example_app:arm64+0x1004c0810)
    #1 0x000101439608 in _resumeUnsafeContinuation<A>(_:_:)+0x20 (example_app:arm64+0x1004bd608)
    #2 0x000103d8db94 in @objc completion handler block implementation for @escaping @callee_unowned @convention(block) () -> () with result type ()+0x110 (example_app:arm64+0x102e11b94)
    #3 0x000104cfe274 in @objc closure #1 in Impl.foo()+0x280 (example_app:arm64+0x103d82274)
    #4 0x0002495b5af0 in swift::runJobInEstablishedExecutorContext(swift::Job*)+0x80 (libswift_Concurrency.dylib:arm64+0x42af0)

e[1me[32m0x000121390550 is located 0 bytes inside of 42-byte region [0x000121390550,0x00012139057a)
e[1me[0me[1me[35mfreed by thread T0 here:e[1me[0m
==33554==WARNING: atos failed to symbolize address "0x11d84140c"
    #0 0x00011e46a1c4 in free+0x70 (libclang_rt.asan_iossim_dynamic.dylib:arm64+0x3a1c4)
    #1 0x000180e9d548 in +[NSObject(NSKeyValueCoding) _createMutableArrayValueGetterWithContainerClassID:key:]+0x530 (Foundation:arm64+0x6c3548)
    #2 0x000180ea35e8 in _NSKeyValueMutableArrayGetterForIsaAndKey+0xcc (Foundation:arm64+0x6c95e8)
    #3 0x000180eac5a0 in _NSKVONotifyingEnableForInfoAndKey+0x3dc (Foundation:arm64+0x6d25a0)
    #4 0x000180eb674c in -[NSKeyValueUnnestedProperty _isaForAutonotifying]+0x34 (Foundation:arm64+0x6dc74c)
    #5 0x000180eb67b8 in -[NSKeyValueUnnestedProperty isaForAutonotifying]+0x50 (Foundation:arm64+0x6dc7b8)
    #6 0x000180eb1414 in -[NSObject(NSKeyValueObserverRegistration) _addObserver:forProperty:options:context:]+0x18c (Foundation:arm64+0x6d7414)
    #7 0x000180eb1aac in -[NSObject(NSKeyValueObserverRegistration) addObserver:forKeyPath:options:context:]+0x80 (Foundation:arm64+0x6d7aac)
    #8 0x00018fe3c2b8 in -[AXSupportDefaultsObserver startObservingPreference:andBroadcastDarwinNotification:postGlobally:]+0xd0 (libAccessibility.dylib:arm64+0x72b8)
    #9 0x00011e46933c in __wrap_dispatch_async_block_invoke+0xb8 (libclang_rt.asan_iossim_dynamic.dylib:arm64+0x3933c)
    #10 0x00011e248cd4 in _dispatch_call_block_and_release+0x14 (libdispatch.dylib:arm64+0x4cd4)
    #11 0x00011e24a5cc in _dispatch_client_callout+0xc (libdispatch.dylib:arm64+0x65cc)
    #12 0x00011e25a418 in _dispatch_main_queue_drain+0x4c8 (libdispatch.dylib:arm64+0x16418)
    #13 0x00011e259f3c in _dispatch_main_queue_callback_4CF+0x24 (libdispatch.dylib:arm64+0x15f3c)
    #14 0x00018041cdb8 in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__+0x8 (CoreFoundation:arm64+0x8fdb8)
    #15 0x000180417314 in __CFRunLoopRun+0x794 (CoreFoundation:arm64+0x8a314)
    #16 0x000180416700 in CFRunLoopRunSpecific+0x224 (CoreFoundation:arm64+0x89700)
    #17 0x000190604b0c in GSEventRunModal+0x9c (GraphicsServices:arm64+0x3b0c)
    #18 0x000185b3917c in -[UIApplication _run]+0x318 (UIKitCore:arm64+0xe6017c)
    #19 0x000185b3d374 in UIApplicationMain+0x78 (UIKitCore:arm64+0xe64374)
    #20 0x000103d04784 in closure #1 in static AppDelegate.main()+0x8c (example_app:arm64+0x102d88784)
    #21 0x0001036ca580 in autoreleasepool<A, B>(invoking:)+0x130 (example_app:arm64+0x10274e580)
    #22 0x000103d04680 in static AppDelegate.main()+0x144 (example_app:arm64+0x102d88680)
    #23 0x000103d059d4 in static AppDelegate.$main()+0x28 (example_app:arm64+0x102d899d4)
    #24 0x000103d05c78 in main+0x18 (example_app:arm64+0x102d89c78)
    #25 0x00011d84140c  (/usr/lib/dyld:arm64+0x140c)
    #26 0x00011d912b48  (<unknown module>)

e[1me[35mpreviously allocated by thread T0 here:e[1me[0m
==33554==WARNING: atos failed to symbolize address "0x11d84140c"
    #0 0x00011e46a0dc in malloc+0x6c (libclang_rt.asan_iossim_dynamic.dylib:arm64+0x3a0dc)
    #1 0x0001801cd7c8 in _malloc_type_malloc_outlined+0x70 (libsystem_malloc.dylib:arm64+0xf7c8)
    #2 0x000180e9d108 in +[NSObject(NSKeyValueCoding) _createMutableArrayValueGetterWithContainerClassID:key:]+0xf0 (Foundation:arm64+0x6c3108)
    #3 0x000180ea35e8 in _NSKeyValueMutableArrayGetterForIsaAndKey+0xcc (Foundation:arm64+0x6c95e8)
    #4 0x000180eac5a0 in _NSKVONotifyingEnableForInfoAndKey+0x3dc (Foundation:arm64+0x6d25a0)
    #5 0x000180eb674c in -[NSKeyValueUnnestedProperty _isaForAutonotifying]+0x34 (Foundation:arm64+0x6dc74c)
    #6 0x000180eb67b8 in -[NSKeyValueUnnestedProperty isaForAutonotifying]+0x50 (Foundation:arm64+0x6dc7b8)
    #7 0x000180eb1414 in -[NSObject(NSKeyValueObserverRegistration) _addObserver:forProperty:options:context:]+0x18c (Foundation:arm64+0x6d7414)
    #8 0x000180eb1aac in -[NSObject(NSKeyValueObserverRegistration) addObserver:forKeyPath:options:context:]+0x80 (Foundation:arm64+0x6d7aac)
    #9 0x00018fe3c2b8 in -[AXSupportDefaultsObserver startObservingPreference:andBroadcastDarwinNotification:postGlobally:]+0xd0 (libAccessibility.dylib:arm64+0x72b8)
    #10 0x00011e46933c in __wrap_dispatch_async_block_invoke+0xb8 (libclang_rt.asan_iossim_dynamic.dylib:arm64+0x3933c)
    #11 0x00011e248cd4 in _dispatch_call_block_and_release+0x14 (libdispatch.dylib:arm64+0x4cd4)
    #12 0x00011e24a5cc in _dispatch_client_callout+0xc (libdispatch.dylib:arm64+0x65cc)
    #13 0x00011e25a418 in _dispatch_main_queue_drain+0x4c8 (libdispatch.dylib:arm64+0x16418)
    #14 0x00011e259f3c in _dispatch_main_queue_callback_4CF+0x24 (libdispatch.dylib:arm64+0x15f3c)
    #15 0x00018041cdb8 in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__+0x8 (CoreFoundation:arm64+0x8fdb8)
    #16 0x000180417314 in __CFRunLoopRun+0x794 (CoreFoundation:arm64+0x8a314)
    #17 0x000180416700 in CFRunLoopRunSpecific+0x224 (CoreFoundation:arm64+0x89700)
    #18 0x000190604b0c in GSEventRunModal+0x9c (GraphicsServices:arm64+0x3b0c)
    #19 0x000185b3917c in -[UIApplication _run]+0x318 (UIKitCore:arm64+0xe6017c)
    #20 0x000185b3d374 in UIApplicationMain+0x78 (UIKitCore:arm64+0xe64374)
    #21 0x000103d04784 in closure #1 in static AppDelegate.main()+0x8c (example_app:arm64+0x102d88784)
    #22 0x0001036ca580 in autoreleasepool<A, B>(invoking:)+0x130 (example_app:arm64+0x10274e580)
    #23 0x000103d04680 in static AppDelegate.main()+0x144 (example_app:arm64+0x102d88680)
    #24 0x000103d059d4 in static AppDelegate.$main()+0x28 (example_app:arm64+0x102d899d4)
    #25 0x000103d05c78 in main+0x18 (example_app:arm64+0x102d89c78)
    #26 0x00011d84140c  (/usr/lib/dyld:arm64+0x140c)
    #27 0x00011d912b48  (<unknown module>)

SUMMARY: AddressSanitizer: heap-use-after-free (example_app:arm64+0x1004c0810) in UnsafeContinuation.resume<>(returning:)+0x48

That was specific to invoking it by passing a selector to NSNotificationCenter. In the general case it could work, but NSNotificationCenter doesn't call asynchronous methods correctly.

Interesting, since 16.3 I have also been experiencing a lot of notification centre crashes. And we aren't calling any async methods from it.

Also seeing some random crashes when calling closures as well.

See Swift 6.1 runtime crash when calling @objc async protocol method in target with mixed Swift 5 and Swift 6 dependencies · Issue #81846 · swiftlang/swift · GitHub for more details and a reproducer app.

I can confirm this still happens on Xcode 16.4 as well