Thursday, June 18, 2015

OS X Yosemite bug: SCardGetStatusChange blocks forever

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

SCardGetStatusChange() blocks forever after some time

SCardGetStatusChange() does not work correctly on Mac OS X 10.10 Yosemite. Sometimes the function blocks and does not return.

The execution do not always block at the same iteration. It looks like an internal dead lock in some circumstances.

See also

The problem was reported to me by Paolo S. Thanks to him.

Apple bug report #21437286 "PC/SC SCardGetStatusChange() blocks forever after some time".
Closed by Apple on 27th June 2015, as duplicate of  #20517200.

Sample code

Code from Paolo S.

#include <stdio.h>
#include <stdlib.h>

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

#define CHECK(f, rv) \
 if (SCARD_S_SUCCESS != rv) \
 { \
  printf(f ": %s\n", pcsc_stringify_error(rv)); \
  return -1; \
 }

int main(void) {
    LONG rv;

    SCARDCONTEXT hContext;
    LPTSTR mszReaders;
    DWORD dwReaders;

    unsigned int i;

    rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
    CHECK("SCardEstablishContext", rv)

    rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
    CHECK("SCardListReaders", rv)

    mszReaders = calloc(dwReaders, sizeof(char));
    rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
    CHECK("SCardListReaders", rv)

    if (dwReaders <= 1) {
        puts("No reader found");
        goto end ;
    }

    printf("reader name: %s\n", mszReaders);

    SCARD_READERSTATE sReaderState ;
    sReaderState.szReader = mszReaders ;

    i = 0 ;
    while(1) {
        i++ ;
        printf("Loop #%d: calling SCardGetStatusChange\n", i);
        rv = SCardGetStatusChange(hContext, 1, &sReaderState, 1);   // This hangs after random number of calls
        puts("returned");
        CHECK("SCardGetStatusChange", rv)
    }

    free(mszReaders);

end:
    rv = SCardReleaseContext(hContext);

    CHECK("SCardReleaseContext", rv)

    return 0;
}

Result (on Yosemite)

$ ./main
reader name: Gemalto PC Twin Reader
Loop #1: calling SCardGetStatusChange
returned
Loop #2: calling SCardGetStatusChange
[...]
Loop #11145: calling SCardGetStatusChange
returned
Loop #11146: calling SCardGetStatusChange
^C

I have to use Control-C to stop the execution.

Known workaround

Paolo S. has a workaround.
  1. detect the card presence by calling the SCardConnect() function (shared mode, default protocols). no errors = card inserted.
  2. detect the card extraction by calling the SCardReconnect() function (same parameters, no card reset or eject). no errors = card still inserted.

Update

This bug is now fixed in Mac OS X El Capitan 10.11.0.