[PATCH 03/27] hw/usb/dev-smartcard-reader: fix assert on pending answers overflow

Marc-AndrĂ© Lureau <[email protected]> Wed, 05 Aug 2026 15:50:53 +0400
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
A guest can send PC_to_RDR_XfrBlock USB messages faster than the async
card backend can process them. When the pending_answers ring buffer
(128 entries) fills, ccid_add_pending_answer() hits an assert() and
crashes QEMU.

The CCID spec (section 4) states that a slot can only accept one
command at a time. A second command to a busy slot must be rejected
with CMD_SLOT_BUSY (section 6.2.6).

Enforce this by checking pending_answers_num before queuing a new
APDU. When the slot is already busy, report ERROR_CMD_SLOT_BUSY and
send a DataBlock error response back to the guest.

Fixes: CVE-2026-18410
Fixes: 367071447ec5 ("usb-ccid: add CCID bus")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3997
Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
---
 hw/usb/dev-smartcard-reader.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 8241cd07648d..9fc6bbca0f2d 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -166,6 +166,7 @@ enum {
     ERROR_XFR_PARITY_ERROR  = -3,
     ERROR_XFR_OVERRUN       = -4,
     ERROR_HW_ERROR          = -5,
+    ERROR_CMD_SLOT_BUSY     = -32,
 };
 
 /* 6.2.6 RDR_to_PC_SlotStatus definitions */
@@ -945,6 +946,13 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
         ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
         return;
     }
+    if (s->pending_answers_num > 0) {
+        DPRINTF(s, D_WARN,
+                "usb-ccid: slot already busy, rejecting apdu\n");
+        ccid_report_error_failed(s, ERROR_CMD_SLOT_BUSY);
+        ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
+        return;
+    }
     len = le32_to_cpu(recv->hdr.dwLength);
     DPRINTF(s, 1, "%s: seq %d, len %u\n", __func__,
                 recv->hdr.bSeq, len);

-- 
2.55.0