Skip to content

Thread safety issue in FoundationEssentials.Calendar #1886

Description

@TwirlySeal

Describe the bug
Accessing Calendar from FoundationEssentials rapidly and concurrently can cause various thread safety issues on Windows and macOS, including access violations and heap corruption.

To Reproduce

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

import Dispatch

@main
struct Bug {
    static func main() {
        DispatchQueue.concurrentPerform(iterations: 100) { i in
            let calendar = Calendar.current
            let timeZone = TimeZone.current
            
            let date = DateComponents(calendar: calendar, timeZone: timeZone)
        }
    }
}

When debugging with LLDB, this output appears:

(lldb) n
Process 24336 stopped
* thread #11, name = 'com.apple.root.default-qos', stop reason = Exception 0xc0000005 encountered at address 0x7ff84bf720c9: Access violation reading location 0xb1805d80
    frame #0: 0x00007ff84bf720c9 swiftCore.dll`swift_retain + 41
swiftCore.dll`swift_retain:
->  0x7ff84bf720c9 <+41>: movq   0x8(%r8), %rax
    0x7ff84bf720cd <+45>: nopl   (%rax)
    0x7ff84bf720d0 <+48>: movq   %rax, %r10
    0x7ff84bf720d3 <+51>: addq   %r9, %r10
Target 0: (Bug.exe) stopped.
(lldb) bt
* thread #11, name = 'com.apple.root.default-qos', stop reason = Exception 0xc0000005 encountered at address 0x7ff84bf720c9: Access violation reading location 0xb1805d80
  * frame #0: 0x00007ff84bf720c9 swiftCore.dll`swift_retain + 41
    frame #1: 0x00007ff7803313cd Bug.exe`closure #1 in static Bug.main(i=4) at Bug.swift:11:69
    frame #2: 0x00007ff8e5209440 swiftDispatch.dll`static DispatchQueue.concurrentPerform(iterations:execute:) + 352
    frame #3: 0x00007ff8e520ae43 swiftDispatch.dll`Dispatch.DispatchQueue.AutoreleaseFrequency.hashValue.getter : Swift.Int + 67
    frame #4: 0x00007ff8d6f310fe dispatch.dll`
    frame #5: 0x00007ff8d6f44dd2 dispatch.dll`_dispatch_prohibit_transition_to_multithreaded + 8802
    frame #6: 0x00007ff9116b37b0 ucrtbase.dll`wcsrchr + 336
    frame #7: 0x00007ff9130ce957 kernel32.dll`BaseThreadInitThunk + 23
    frame #8: 0x00007ff914b0421c ntdll.dll`RtlUserThreadStart + 44

Expected behavior
No access violations occur during the execution of the program

Configuration

  • Swift Version: Swift 6.3 (Release)
  • OS: Windows and macOS
  • OS Version: Windows 11 (25H2) and macOS 26

Regression information:
This issue also occurs on Swift 6.2.3, but I haven't tested other versions.

Additional context

  • I saw the recent PR 'Replace LockedState with Mutex' which updated the Calendar cache; I do not know if it fixes this issue.
  • The program where I first encountered this bug exited silently with the Heap Corruption status code on Windows and the environment variable _NO_DEBUG_HEAP=1 was needed to reproduce the issue while debugging with LLDB. An excerpt of the LLDB output is below:
(lldb) n
Process 29660 stopped
* thread #10, name = 'com.apple.root.default-qos.overcommit', stop reason = Exception 0x80000003 encountered at address 0x7ff914b98f28
    frame #0: 0x00007ff914b98f29 ntdll.dll`LdrResSearchResource + 2041
ntdll.dll`LdrResSearchResource:
->  0x7ff914b98f29 <+2041>: jmp    0x7ff914b98f37 ; <+2055>
    0x7ff914b98f2b <+2043>: movq   0xf8(%rsp), %rdi
    0x7ff914b98f33 <+2051>: movl   0x20(%rsp), %ebx
    0x7ff914b98f37 <+2055>: movl   %ebx, 0x30(%rsp)
Target 0: (Generator.exe) stopped.
(lldb) bt
* thread #10, name = 'com.apple.root.default-qos.overcommit', stop reason = Exception 0x80000003 encountered at address 0x7ff914b98f28
  * frame #0: 0x00007ff914b98f29 ntdll.dll`LdrResSearchResource + 2041
    frame #1: 0x00007ff914b31db2 ntdll.dll`RtlSetProcessPreferredUILanguages + 2978
    frame #2: 0x00007ff914c1d86a ntdll.dll`RtlLengthCurrentClearRunForwardEx + 1050
    frame #3: 0x00007ff914b2a39b ntdll.dll`RtlCreateUnicodeString + 9083
    frame #4: 0x00007ff914b26c45 ntdll.dll`RtlFreeHeap + 645
    frame #5: 0x00007ff9116ce0fb ucrtbase.dll`_free_base + 27
    frame #6: 0x00007ff84bf7337f swiftCore.dll`swift_unownedCheck + 287
    frame #7: 0x00007ff84bf73c3a swiftCore.dll`swift_weakTakeAssign + 378
    frame #8: 0x00007ff84bcac551 swiftCore.dll`Swift._DictionaryStorage.deinit + 257
    frame #9: 0x00007ff84bcac6e9 swiftCore.dll`Swift._DictionaryStorage.__deallocating_deinit + 9
    frame #10: 0x00007ff84bf7337f swiftCore.dll`swift_unownedCheck + 287
    frame #11: 0x00007ff84bf73c3a swiftCore.dll`swift_weakTakeAssign + 378
    frame #12: 0x00007ff84342d6d9 FoundationEssentials.dll`_timeZoneIdentifier(forWindowsIdentifier:) + 617
    frame #13: 0x00007ff84342e246 FoundationEssentials.dll`_timeZoneIdentifier(forWindowsIdentifier:) + 3542
    frame #14: 0x00007ff843191ab0 FoundationEssentials.dll`LockedState.withLockUnchecked(_:) + 80
    frame #15: 0x00007ff843191a59 FoundationEssentials.dll`LockedState.withLock(_:) + 9
    frame #16: 0x00007ff84342e1e4 FoundationEssentials.dll`_timeZoneIdentifier(forWindowsIdentifier:) + 3444
    frame #17: 0x00007ff843242c7c FoundationEssentials.dll`FoundationEssentials.Calendar.Component.nextHigherUnit.getter : Swift.Optional<FoundationEssentials.Calendar.Component> + 31116
    frame #18: 0x00007ff843253046 FoundationEssentials.dll`FoundationEssentials.Calendar.Component.nextHigherUnit.getter : Swift.Optional<FoundationEssentials.Calendar.Component> + 97622
    frame #19: 0x00007ff8432301e6 FoundationEssentials.dll`_calendarICUClass() + 838

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions