macOS HighSierra bug: SCardEndTransaction() returns SCARD_W_RESET_CARD instead of SCARD_W_REMOVED_CARD

This is part of the series: "macOS High Sierra and smart cards: known bugs"

SCardEndTransaction() returns different error codes than on GNU/Linux and Windows

SCardEndTransaction() on High Sierra returns error codes that are different than on GNU/Linux and Windows.

SCardEndTransaction() returns SCARD_W_RESET_CARD after a card change

On High Sierra SCardEndTransaction() returns SCARD_W_RESET_CARD if the card has been removed and inserted again in the reader.

On GNU/Linux and Windows SCardEndTransaction() returns SCARD_W_REMOVED_CARD instead.

This behaviour can be very confusing for an application. The application has started a PC/SC transaction so it has an exclusive access to the card. No other application can reset the card during a transaction.

See also

Apple bug report #44672548 "PCSC SCardEndTransaction() returns SCARD_W_RESET_CARD instead of SCARD_W_REMOVED_CARD". Marked as duplicate of #23900844.

This bug is very similar to previous bugs for other PC/SC functions:

Sample code

#include <stdio.h>
#include <string.h>
#include <unistd.h>

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

#define CHECK_RV(fct) if (SCARD_S_SUCCESS != rv) { printf(fct"() failed: %s (0x%08X)\n", pcsc_stringify_error(rv), rv); ret = 0; goto error; } else { printf(fct"(): OK\n"); }

int main(void)
{
    int ret = 1;
    SCARDCONTEXT hContext;
    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    LONG rv;
    char mszReaders[1024];
    DWORD dwReaders = sizeof(mszReaders);

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

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

    rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED,
        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard,
        &dwActiveProtocol);
    CHECK_RV("SCardConnect");

    rv = SCardBeginTransaction(hCard);
    CHECK_RV("SCardBeginTransaction");

    printf("Remove and insert the card. Then press Enter");
    getchar();

    rv = SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
    CHECK_RV("SCardEndTransaction");

    rv = SCardDisconnect(hCard, SCARD_UNPOWER_CARD);
    CHECK_RV("SCardDisconnect")

    rv = SCardReleaseContext(hContext);
    CHECK_RV("SCardReleaseContext")

error:
    return ret;
}

Result (on High Sierra)

$ ./main_Mac 
SCardEstablishContext(): OK
SCardListReaders(): OK
SCardConnect(): OK
SCardBeginTransaction(): OK
Remove and insert the card. Then press Enter
SCardEndTransaction() failed: Card was reset. (0x80100068)

Expected result (on Debian)

$ ./main_Linux 
SCardEstablishContext(): OK
SCardListReaders(): OK
SCardConnect(): OK
SCardBeginTransaction(): OK
Remove and insert the card. Then press Enter
SCardEndTransaction() failed: Card was removed. (0x80100069)

Known workaround

None known.