scd: Internal CCID driver: Fix a race condition on close.

* scd/ccid-driver.c (ccid_require_get_status): For VENDOR_SCM reader,
return 0 only at the initial call.
(bulk_in): Don't detect an error for VENDOR_SCM reader, just kicking
the loop, to invoke scd_update_reader_status_file, which calls
ccid_slot_status again.
(ccid_slot_status): Move the call of ccid_vendor_specific_setup to...
(ccid_get_atr): ... here.

--

For readers with interrupt transfer support, it is only intr_cb which
sets handle->powered_off to 1.  Keeping this condition makes no race.
The function ccid_slot_status can also detect a communication error,
which causes apdu_close_reader (but not setting ->powered_off).

GnuPG-bug-id: 5121
Fixes-commit: 920f258eb6
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2020-11-05 15:10:18 +09:00
parent f5a81953e1
commit 484bafda4d
1 changed files with 26 additions and 13 deletions

View File

@ -1787,7 +1787,25 @@ ccid_require_get_status (ccid_driver_t handle)
detect removal of a card and can detect removal of a reader. detect removal of a card and can detect removal of a reader.
*/ */
if (handle->ep_intr >= 0) if (handle->ep_intr >= 0)
return 0; {
if (handle->id_vendor != VENDOR_SCM)
return 0;
/*
* For card reader with interrupt transfer support, ideally,
* removal is detected by intr_cb, but some card reader
* (e.g. SPR532) has a possible case of missing report to
* intr_cb, and another case of valid report to intr_cb.
*
* For such a reader, the removal should be able to be detected
* by PC_to_RDR_GetSlotStatus, too. Thus, calls to
* ccid_slot_status should go on wire even if "on_wire" is not
* requested.
*
*/
if (handle->transfer == NULL)
return 0;
}
/* Libusb actually detects the removal of USB device in use. /* Libusb actually detects the removal of USB device in use.
However, there is no good API to handle the removal (yet), However, there is no good API to handle the removal (yet),
@ -2148,19 +2166,16 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
/* /*
* Communication failure by device side. * Communication failure by device side.
* Possibly, it was forcibly suspended and resumed. * Possibly, it was forcibly suspended and resumed.
*
* For card reader with interrupt transfer support, ideally,
* removal is detected by intr_cb, but some card reader
* (e.g. SPR532) has a case of missing report to intr_cb.
*/ */
if (handle->ep_intr < 0 || handle->id_vendor == VENDOR_SCM) if (handle->ep_intr < 0)
{ {
DEBUGOUT ("CCID: card inactive/removed\n"); DEBUGOUT ("CCID: card inactive/removed\n");
handle->powered_off = 1; handle->powered_off = 1;
#if defined(GNUPG_MAJOR_VERSION)
scd_kick_the_loop ();
#endif
} }
#if defined(GNUPG_MAJOR_VERSION)
scd_kick_the_loop ();
#endif
} }
return rc; return rc;
@ -2427,10 +2442,7 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits, int on_wire)
/* Setup interrupt transfer at the initial call of slot_status /* Setup interrupt transfer at the initial call of slot_status
with ON_WIRE == 0 */ with ON_WIRE == 0 */
if (handle->transfer == NULL) if (handle->transfer == NULL)
{ ccid_setup_intr (handle);
ccid_setup_intr (handle);
ccid_vendor_specific_setup (handle);
}
*statusbits = 0; *statusbits = 0;
return 0; return 0;
@ -2899,6 +2911,7 @@ ccid_get_atr (ccid_driver_t handle,
DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
} }
ccid_vendor_specific_setup (handle);
return 0; return 0;
} }