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
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.