Hello,
We're experiencing intermittent crashes in AuthTokenAuthorizer.getHttpAuthorizationHeaders(request:) caused by a thread-safety issue with DateFormatter.
Crash stack trace:
Crashed: com.apple.root.user-initiated-qos.cooperative
0 libobjc.A.dylib 0x2414 objc_msgSend + 20
1 Foundation 0x43214 -[NSDateFormatter stringForObjectValue:] + 124
2 0x117af58 AuthTokenAuthorizer.getHttpAuthorizationHeaders(request:) + 33 (AuthTokenAuthorizer.swift:33)
3 libswift_Concurrency.dylib 0x2dc30 swift::runJobInEstablishedExecutorContext(swift::Job*) + 304
4 libswift_Concurrency.dylib 0x2e9d8 swift_job_runImpl(swift::Job*, swift::ExecutorRef) + 68
5 libdispatch.dylib 0x48c68 _dispatch_root_queue_drain + 328
6 libdispatch.dylib 0x49430 _dispatch_worker_thread2 + 160
7 libsystem_pthread.dylib 0x1b94 _pthread_wqthread + 224
8 libsystem_pthread.dylib 0x1720 start_wqthread + 8
Root cause:
In AuthTokenAuthorizer.swift, the formatter property is declared as a lazy var on a non-isolated class:
lazy var formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = AuthTokenAuthorizer.AWSDateISO8601DateFormat2
return formatter
}()
This is then used in getHttpAuthorizationHeaders(request:), which is an async method and can therefore be called concurrently from Swift's cooperative thread pool.
There are two issues here:
-
lazy var is not thread-safe — concurrent first access from multiple threads leads to undefined behavior.
-
DateFormatter is not thread-safe — as documented by Apple, concurrent calls to its formatting methods can corrupt its internal state, leading to the objc_msgSend crash we observe.
Suggested fix:
Create a local DateFormatter instance inside the method instead of sharing one across calls:
public func getHttpAuthorizationHeaders(request: URLRequest) async throws -> [String: String] {
let formatter = DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = AuthTokenAuthorizer.AWSDateISO8601DateFormat2
let date = formatter.string(from: Date())
let token = try await fetchLatestAuthToken()
return [AuthTokenAuthorizer.amzDateHeaderName: date,
AuthTokenAuthorizer.authorizationHeaderName: token]
}
This eliminates any concurrency issue with negligible performance impact, since this method already performs a network call.
Environment:
iOS 17+
Swift 6
aws-appsync-apollo-extensions-swift 1.0.5
Thanks
Rudy
Hello,
We're experiencing intermittent crashes in
AuthTokenAuthorizer.getHttpAuthorizationHeaders(request:)caused by a thread-safety issue withDateFormatter.Crash stack trace:
Root cause:
In AuthTokenAuthorizer.swift, the formatter property is declared as a lazy var on a non-isolated class:
This is then used in
getHttpAuthorizationHeaders(request:), which is an async method and can therefore be called concurrently from Swift's cooperative thread pool.There are two issues here:
lazy varis not thread-safe — concurrent first access from multiple threads leads to undefined behavior.DateFormatteris not thread-safe — as documented by Apple, concurrent calls to its formatting methods can corrupt its internal state, leading to theobjc_msgSendcrash we observe.Suggested fix:
Create a local
DateFormatterinstance inside the method instead of sharing one across calls:This eliminates any concurrency issue with negligible performance impact, since this method already performs a network call.
Environment:
iOS 17+
Swift 6
aws-appsync-apollo-extensions-swift 1.0.5
Thanks
Rudy