OS X Yosemite bug: PC/SC functions crash after a fork(2)

This is part of the series: "OS X Yosemite and smart cards: known bugs".

All (?) PC/SC functions

SCardxx() do not work on Yosemite if executed in a son process of the one that has executed SCardEstablishContext().

This happen if SCardEstablishContext() is exected and then the process is forked. The PC/SC functions used in the son using the hContext received by the SCardEstablishContext() in the parent process will crash.

I verified this behaviour only with SCardListReaders() and SCardConnect(). I would not be surprised if it is the case for all PC/SC functions (except SCardEstablishContext() of course).

Using a PC/SC context in a son process after a fork(2) may not be supported by the PC/SC API. The support of such a use case has changed in the official pcsc-lite. The current version (pcsc-lite 1.8.13) will support it but be sure to not use the PC/SC handle in the parent process if you want to use it in the son process (or you will have problems).
In the case of Yosemite the PC/SC function will even crash instead of returning an error code.

See also

Apple bug report #19374107 "PC/SC functions crash after a fork(2)".

"com.apple.pcsc Crashes in SCardDisconnect / transact / libdispatch / fork resource handling code" https://smartcardservices.macosforge.org/trac/ticket/141.

"OpenVPN Crashes in OpenSC pcsc_disconnect on OSX 10.10 Yosemite" https://github.com/OpenSC/OpenSC/issues/333.

Sample code

#include <stdio.h>
#include <unistd.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(int argc, const char * argv[]) {
SCARDCONTEXT hContext;
DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
if (err != SCARD_S_SUCCESS) {
printf("ScardEstablishedContext: 0x%08x\n",err);
return -1;
}

pid_t pid = fork();
printf("%s:%d %d\n", __FILE__, __LINE__, pid);
if (0 == pid)
{
/* son process */
printf("Before\n");
DWORD cchReaders = 0;
err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
if (err != 0) {
printf("ScardListReaders: 0x%08x\n",err);
return -1;
}
printf("Has not crashed: %X\n", err);
}
else
{
/* give some time to the son */
sleep(1);
}

SCardReleaseContext(hContext);

return 0;
}


Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC main.c -o main

$ ./main
main.c:19 8236
main.c:19 0
Before

I could not generate a crash dump or a backtrace using lldb.
You can see that the line "Has not crashed: xx" is NOT displayed. The function SCardListReaders() has crashed and has not returned.

I can reproduce the problem and generate a crash dump using the pcsc-lite unitary test SCard_fork.py. This is less easy to use since you have to install the PC/SC Python wrapper pyscard first.

The crash dump is then:
Process:               Python [8270]
Path: /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Identifier: Python
Version: 2.7.6 (2.7.6)
Code Type: X86-64 (Native)
Parent Process: Python [8268]
Responsible: Terminal [450]
User ID: 501

PlugIn Path: /Library/Python/2.7/site-packages/smartcard/scard/_scard.so
PlugIn Identifier: _scard.so
PlugIn Version: ??? (0)

Date/Time: 2015-01-05 15:54:21.335 +0100
OS Version: Mac OS X 10.10.1 (14B25)
Report Version: 11
Anonymous UUID: 7FE6A9DE-5002-1B38-88FE-227046540C73

Sleep/Wake UUID: 99799303-CF96-4849-9024-4B487D8B2393

Time Awake Since Boot: 7600 seconds
Time Since Wake: 3600 seconds

Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000110

VM Regions Near 0x110:
-->
__TEXT 000000010837b000-000000010837c000 [ 4K] r-x/rwx SM=COW /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python

Application Specific Information:
crashed on child side of fork pre-exec

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libdispatch.dylib 0x00007fff8ddae5d2 _dispatch_queue_wakeup_with_qos_slow + 525
1 libdispatch.dylib 0x00007fff8ddae5f8 _dispatch_queue_wakeup_with_qos_slow + 563
2 libdispatch.dylib 0x00007fff8dda3cfc _dispatch_mach_msg_send + 1690
3 libdispatch.dylib 0x00007fff8dda35e4 dispatch_mach_send + 326
4 libxpc.dylib 0x00007fff887048b1 _xpc_connection_send_message_with_reply_f + 125
5 libxpc.dylib 0x00007fff887047c2 xpc_connection_send_message_with_reply_sync + 185
6 com.apple.pcsc 0x00000001086fec5b transact + 604
7 com.apple.pcsc 0x00000001086ffe73 SCardListReaders + 140
8 _scard.so 0x00000001086ec6a5 _wrap_SCardListReaders + 181 (scard_wrap.c:3401)
9 org.python.python 0x0000000108407180 PyEval_EvalFrameEx + 12778
10 org.python.python 0x0000000108403d62 PyEval_EvalCodeEx + 1413
11 org.python.python 0x000000010840a57d 0x108380000 + 566653
12 org.python.python 0x00000001084073e3 PyEval_EvalFrameEx + 13389
13 org.python.python 0x0000000108403d62 PyEval_EvalCodeEx + 1413
14 org.python.python 0x00000001084037d7 PyEval_EvalCode + 54
15 org.python.python 0x00000001084237bd 0x108380000 + 669629
16 org.python.python 0x0000000108423860 PyRun_FileExFlags + 133
17 org.python.python 0x00000001084233fd PyRun_SimpleFileExFlags + 769
18 org.python.python 0x0000000108434b23 Py_Main + 3051
19 libdyld.dylib 0x00007fff8ca555c9 start + 1

Thread 0 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000000 rbx: 0x00007f97b2517a50 rcx: 0x0000000000000100 rdx: 0x0000000000000001
rdi: 0x00007f97b244f620 rsi: 0x0000000000000001 rbp: 0x00007fff57883990 rsp: 0x00007fff57883960
r8: 0x0000000000000006 r9: 0x00000000fffff000 r10: 0x00007fff7584b244 r11: 0x0000000000000206
r12: 0x0000000000000001 r13: 0x00007f97b244f620 r14: 0x0000000000001000 r15: 0x00007fff76583bc0
rip: 0x00007fff8ddae5d2 rfl: 0x0000000000010206 cr2: 0x0000000000000110

Logical CPU: 1
Error Code: 0x00000006
Trap Number: 14

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC -lpcsclite main.c -o main

$ ./main
main.c:19 2274
main.c:19 0
Before
Has not crashed: 0

Here the line "Has not crashed: 0" is displayed and SCardListReaders() returned SCARD_S_SUCCESS.

Known workaround

Do not share a PC/SC context between a father process and its sons. It is a dangerous use the of PC/SC API. In the case of Yosemite it will even make your application crash.

Update, 4 April 2015

A reader signals that the Mac OS X behaviour is normal according to fork(2) manpage.

CAVEATS
There are limits to what you can do in the child process. To be totally safe you should restrict yourself to only executing async-signal safe operations until such time as one of the exec functions is called. All APIs, including global data symbols, in any framework or library should be assumed to be unsafe after a fork() unless explicitly documented to be safe or async-signal safe. If you need to use these frameworks in the child process, you must exec. In this situation it is reasonable to exec yourself.

Mavericks

Maybe it is an expected behaviour. But it is a regression compared to Mavericks (Mac OS X 10.9).

On Mavericks the output is:
$ ./main
a.c:19 2297
a.c:19 0
Before
Has not crashed: 0
Previous
Next Post »