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
This commit is contained in:
heinz-goetz
2026-09-25 09:14:58 +02:00
committed by GitHub
parent b48e31f5b4
commit 55920deffb
+9 -3
View File
@@ -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)