Skip to content

Thread safety issue in FoundationEssentials.Calendar #1886

@TwirlySeal

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

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