From 55920deffb5fc7060c69492545e9e0ba00dfb4ab Mon Sep 17 00:00:00 2001 From: heinz-goetz Date: Fri, 25 Sep 2026 09:14:58 +0200 Subject: [PATCH] macOS: initialize PC/SC lazily to keep fork() children clean (#1887) SCard::manager is a static object whose constructor called SCardLoader::Initialize(), which establishes a PC/SC context before main() in every VeraCrypt process. On macOS this opens an XPC connection that starts a helper thread and marks libdispatch as fork-unsafe. As a result, CoreService::Start() forked a multithreaded process, and the FUSE service (which libfuse runs after fork() without exec()) inherited armed Objective-C fork-safety checks and poisoned dispatch queues. With macFUSE >= 5.3.3 this causes: - a SIGABRT when mounting ("+[NSNumber initialize] may have been in progress in another thread when fork() was called"), and - a SIGSEGV in MFChannelClose/dispatch_channel_cancel at unmount. Load the PC/SC library on first use instead: GetReaders() now calls loader->Initialize() itself (GetReader() already did, and Initialize() is idempotent). PC/SC is then only touched when EMV keyfiles are used, and never in the core service or FUSE service processes. Tested on macOS 27.0 (arm64) with macFUSE 5.4.0: 30/30 mount/write/ remount/verify/dismount cycles with no crash reports. Previously every mount failed. Refs #1884, #1863, macfuse/macfuse#1193 Assisted-by: Claude Opus 5.5 --- src/Common/SCardManager.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Common/SCardManager.cpp b/src/Common/SCardManager.cpp index cb97c1a0..3a714b73 100644 --- a/src/Common/SCardManager.cpp +++ b/src/Common/SCardManager.cpp @@ -7,9 +7,13 @@ namespace VeraCrypt SCardManager::SCardManager() { -#ifndef TC_OPENBSD - loader->Initialize(); -#endif + // The PC/SC library is loaded lazily on first use (see GetReaders/GetReader). + // SCardManager instances are static objects, so initializing here would run + // before main(). On macOS, establishing a PC/SC context opens an XPC connection, + // which starts a helper thread and marks libdispatch as fork-unsafe. The core + // service fork() would then happen in a multithreaded process, and the FUSE + // service (which libfuse runs after fork() without exec()) would inherit + // poisoned dispatch queues and armed Objective-C fork-safety checks. } SCardManager::~SCardManager() @@ -28,6 +32,8 @@ namespace VeraCrypt SCARDCONTEXT hScardContext = 0; LONG lRet = SCARD_S_SUCCESS; + loader->Initialize(); + hScardContext = loader->GetSCardContext(); lRet = loader->SCardIsValidContext(hScardContext); if (SCARD_S_SUCCESS != lRet)