qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 4/8] usb-ccid: Fix ATR parsing


From: Stefan Fritsch
Subject: [Qemu-devel] [PATCH v2 4/8] usb-ccid: Fix ATR parsing
Date: Thu, 20 Jul 2017 11:02:47 +0200

From: Stefan Fritsch <address@hidden>

- The number of parameter set TA_i...TD_i is unlimited. The list ends
  if TD_i is not present or the high nibble is zero.
- If at least one protocol in any of the TD bytes is non-zero, the
  ATR must have a checksum.
- Add a missing length check before accessing TD.
- Fixup a wrong checksum but accept the ATR.

Signed-off-by: Stefan Fritsch <address@hidden>
Signed-off-by: Christian Ehrhardt <address@hidden>
---
 hw/usb/ccid-card-passthru.c | 34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 45d96b03c6..e2e94ac1ba 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -146,8 +146,7 @@ static void ccid_card_vscard_handle_init(
 
 static int check_atr(PassthruState *card, uint8_t *data, int len)
 {
-    int historical_length, opt_bytes;
-    int td_count = 0;
+    int historical_length, opt_bytes, tck = 0;
     int td;
 
     if (len < 2) {
@@ -160,10 +159,8 @@ static int check_atr(PassthruState *card, uint8_t *data, 
int len)
                 data[0]);
         return 0;
     }
-    td_count = 0;
     td = data[1] >> 4;
-    while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) {
-        td_count++;
+    while (td && opt_bytes + historical_length + 2 < len) {
         if (td & 0x1) {
             opt_bytes++;
         }
@@ -175,16 +172,37 @@ static int check_atr(PassthruState *card, uint8_t *data, 
int len)
         }
         if (td & 0x8) {
             opt_bytes++;
-            td = data[opt_bytes + 2] >> 4;
+            if (opt_bytes + 1 >= len) {
+                break;
+            }
+            /* Checksum byte must be present if T!=0 */
+            if (data[opt_bytes + 1] & 0xf) {
+                tck = 1;
+            }
+            td = data[opt_bytes + 1] >> 4;
+        } else {
+            td = 0;
         }
     }
-    if (len < 2 + historical_length + opt_bytes) {
+    if (len < 2 + historical_length + opt_bytes + tck) {
         DPRINTF(card, D_WARN,
             "atr too short: len %d, but historical_len %d, T1 0x%X\n",
             len, historical_length, data[1]);
         return 0;
     }
-    if (len > 2 + historical_length + opt_bytes) {
+    if (tck) {
+        int i;
+        uint8_t cksum = 0;
+        for (i = 1; i < 2 + historical_length + opt_bytes; ++i) {
+            cksum ^= data[i];
+        }
+        if (cksum != data[i]) {
+            DPRINTF(card, D_WARN, "atr has bad checksum: "
+                "real=0x%x should be 0x%x (FIXED)\n", cksum, data[i]);
+            data[i] = cksum;
+        }
+    }
+    if (len > 2 + historical_length + opt_bytes + tck) {
         DPRINTF(card, D_WARN,
             "atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n",
             len, historical_length, opt_bytes, data[1]);
-- 
2.11.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]