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 executedSCardEstablishContext()
.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
Update, 14 November 2015
Answer from Apple to my bug report:Please know this is intended behavior. Please review the CAVEATS section in the fork() manpage.
To clarify: It is intended that the results are undefined & unsupported for using Darwin APIs beyond the POSIX standard in a forked child of a parent process that had also used Darwin APIs beyond the POSIX standard. In such cases we try to at least make the crash report contain a clear indication that the program attempted such unsupported behavior. In this case the crash doesn’t have any such indication; for that we apologize.