[PATCH 13/27] hw/usb/dev-smartcard-reader: reject oversized responses safely

Marc-AndrĂ© Lureau <[email protected]> Wed, 05 Aug 2026 15:51:03 +0400
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
ccid_reserve_recv_buf() takes a uint16_t length, so the result of
sizeof(CCID_DataBlock) + len silently truncates when the sum exceeds
65535.  This could bypass the BULK_IN_BUF_SIZE check inside the
function, leading to a buffer overrun on the fixed-size BulkIn.data
array.

The response length comes from the card backend (passthru chardev or
libcacard), not from the guest, so this is not guest-exploitable but
still worth hardening against a buggy or compromised backend.

Fix this by adding explicit overflow-safe checks in both callers that
add a header to the payload: ccid_write_data_block() returns false on
oversized input, and ccid_write_data_block_answer() reports
ERROR_HW_ERROR to the guest and sends a zero-length data block so the
answer queue stays in sync.

Fixes: 367071447ec5 ("usb-ccid: add CCID bus")
Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
---
 hw/usb/dev-smartcard-reader.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 16f248a0969f..d91376826c5c 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -769,7 +769,13 @@ static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv)
 static bool ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq,
                                   const uint8_t *data, uint32_t len)
 {
-    CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len);
+    CCID_DataBlock *p;
+
+    if (len > BULK_IN_BUF_SIZE - sizeof(*p)) {
+        DPRINTF(s, D_WARN, "data block is too large (%u bytes)\n", len);
+        return false;
+    }
+    p = ccid_reserve_recv_buf(s, sizeof(*p) + len);
 
     if (p == NULL) {
         return false;
@@ -813,6 +819,14 @@ static bool ccid_write_data_block_answer(USBCCIDState *s,
         return false;
     }
     answer = ccid_peek_next_answer(s);
+    if (len > BULK_IN_BUF_SIZE - sizeof(CCID_DataBlock)) {
+        DPRINTF(s, D_WARN,
+                "APDU response is too large (%u bytes), returning an error\n",
+                len);
+        ccid_report_error_failed(s, ERROR_HW_ERROR);
+        data = NULL;
+        len = 0;
+    }
     if (!ccid_write_data_block(s, answer->slot, answer->seq, data, len)) {
         return false;
     }

-- 
2.55.0