Browse Source

Merge remote-tracking branch 'remotes/philmd/tags/sdmmc-20210712' into staging

SD/MMC patches queue

- sdcard: Check for valid address range in SEND_WRITE_PROT (CMD30)

# gpg: Signature made Mon 12 Jul 2021 11:28:13 BST
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd/tags/sdmmc-20210712:
  hw/sd/sdcard: Check for valid address range in SEND_WRITE_PROT (CMD30)
  hw/sd/sdcard: Extract address_in_range() helper, log invalid accesses
  hw/sd/sdcard: When card is in wrong state, log which state it is

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Peter Maydell 4 years ago
parent
commit
eca7371335
4 changed files with 97 additions and 14 deletions
  1. 2 1
      MAINTAINERS
  2. 28 13
      hw/sd/sd.c
  3. 66 0
      tests/qtest/fuzz-sdcard-test.c
  4. 1 0
      tests/qtest/meson.build

+ 2 - 1
MAINTAINERS

@@ -1823,7 +1823,8 @@ F: include/hw/sd/sd*
 F: hw/sd/core.c
 F: hw/sd/sd*
 F: hw/sd/ssi-sd.c
-F: tests/qtest/sd*
+F: tests/qtest/fuzz-sdcard-test.c
+F: tests/qtest/sdhci-test.c
 
 USB
 M: Gerd Hoffmann <kraxel@redhat.com>

+ 28 - 13
hw/sd/sd.c

@@ -937,6 +937,19 @@ static void sd_lock_command(SDState *sd)
         sd->card_status &= ~CARD_IS_LOCKED;
 }
 
+static bool address_in_range(SDState *sd, const char *desc,
+                             uint64_t addr, uint32_t length)
+{
+    if (addr + length > sd->size) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s offset %"PRIu64" > card %"PRIu64" [%%%u]\n",
+                      desc, addr, sd->size, length);
+        sd->card_status |= ADDRESS_ERROR;
+        return false;
+    }
+    return true;
+}
+
 static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
 {
     uint32_t rca = 0x0000;
@@ -1218,8 +1231,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
         switch (sd->state) {
         case sd_transfer_state:
 
-            if (addr + sd->blk_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
+            if (!address_in_range(sd, "READ_BLOCK", addr, sd->blk_len)) {
                 return sd_r1;
             }
 
@@ -1264,8 +1276,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
         switch (sd->state) {
         case sd_transfer_state:
 
-            if (addr + sd->blk_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
+            if (!address_in_range(sd, "WRITE_BLOCK", addr, sd->blk_len)) {
                 return sd_r1;
             }
 
@@ -1325,8 +1336,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
 
         switch (sd->state) {
         case sd_transfer_state:
-            if (addr >= sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
+            if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) {
                 return sd_r1b;
             }
 
@@ -1348,8 +1358,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
 
         switch (sd->state) {
         case sd_transfer_state:
-            if (addr >= sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
+            if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) {
                 return sd_r1b;
             }
 
@@ -1371,6 +1380,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
 
         switch (sd->state) {
         case sd_transfer_state:
+            if (!address_in_range(sd, "SEND_WRITE_PROT",
+                                  req.arg, sd->blk_len)) {
+                return sd_r1;
+            }
+
             sd->state = sd_sendingdata_state;
             *(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
             sd->data_start = addr;
@@ -1504,7 +1518,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
         return sd_illegal;
     }
 
-    qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd);
+    qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state: %s\n",
+                  req.cmd, sd_state_name(sd->state));
     return sd_illegal;
 }
 
@@ -1825,8 +1840,8 @@ void sd_write_byte(SDState *sd, uint8_t value)
     case 25:	/* CMD25:  WRITE_MULTIPLE_BLOCK */
         if (sd->data_offset == 0) {
             /* Start of the block - let's check the address is valid */
-            if (sd->data_start + sd->blk_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
+            if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK",
+                                  sd->data_start, sd->blk_len)) {
                 break;
             }
             if (sd->size <= SDSC_MAX_CAPACITY) {
@@ -1998,8 +2013,8 @@ uint8_t sd_read_byte(SDState *sd)
 
     case 18:	/* CMD18:  READ_MULTIPLE_BLOCK */
         if (sd->data_offset == 0) {
-            if (sd->data_start + io_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
+            if (!address_in_range(sd, "READ_MULTIPLE_BLOCK",
+                                  sd->data_start, io_len)) {
                 return 0x00;
             }
             BLK_READ_BLOCK(sd->data_start, io_len);

+ 66 - 0
tests/qtest/fuzz-sdcard-test.c

@@ -0,0 +1,66 @@
+/*
+ * QTest fuzzer-generated testcase for sdcard device
+ *
+ * Copyright (c) 2021 Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/libqtest.h"
+
+/*
+ * https://gitlab.com/qemu-project/qemu/-/issues/450
+ * Used to trigger:
+ *  Assertion `wpnum < sd->wpgrps_size' failed.
+ */
+static void oss_fuzz_29225(void)
+{
+    QTestState *s;
+
+    s = qtest_init(" -display none -m 512m -nodefaults -nographic"
+                   " -device sdhci-pci,sd-spec-version=3"
+                   " -device sd-card,drive=d0"
+                   " -drive if=none,index=0,file=null-co://,format=raw,id=d0");
+
+    qtest_outl(s, 0xcf8, 0x80001010);
+    qtest_outl(s, 0xcfc, 0xd0690);
+    qtest_outl(s, 0xcf8, 0x80001003);
+    qtest_outl(s, 0xcf8, 0x80001013);
+    qtest_outl(s, 0xcfc, 0xffffffff);
+    qtest_outl(s, 0xcf8, 0x80001003);
+    qtest_outl(s, 0xcfc, 0x3effe00);
+
+    qtest_bufwrite(s, 0xff0d062c, "\xff", 0x1);
+    qtest_bufwrite(s, 0xff0d060f, "\xb7", 0x1);
+    qtest_bufwrite(s, 0xff0d060a, "\xc9", 0x1);
+    qtest_bufwrite(s, 0xff0d060f, "\x29", 0x1);
+    qtest_bufwrite(s, 0xff0d060f, "\xc2", 0x1);
+    qtest_bufwrite(s, 0xff0d0628, "\xf7", 0x1);
+    qtest_bufwrite(s, 0x0, "\xe3", 0x1);
+    qtest_bufwrite(s, 0x7, "\x13", 0x1);
+    qtest_bufwrite(s, 0x8, "\xe3", 0x1);
+    qtest_bufwrite(s, 0xf, "\xe3", 0x1);
+    qtest_bufwrite(s, 0xff0d060f, "\x03", 0x1);
+    qtest_bufwrite(s, 0xff0d0605, "\x01", 0x1);
+    qtest_bufwrite(s, 0xff0d060b, "\xff", 0x1);
+    qtest_bufwrite(s, 0xff0d060c, "\xff", 0x1);
+    qtest_bufwrite(s, 0xff0d060e, "\xff", 0x1);
+    qtest_bufwrite(s, 0xff0d060f, "\x06", 0x1);
+    qtest_bufwrite(s, 0xff0d060f, "\x9e", 0x1);
+
+    qtest_quit(s);
+}
+
+int main(int argc, char **argv)
+{
+    const char *arch = qtest_get_arch();
+
+    g_test_init(&argc, &argv, NULL);
+
+   if (strcmp(arch, "i386") == 0) {
+        qtest_add_func("fuzz/sdcard/oss_fuzz_29225", oss_fuzz_29225);
+   }
+
+   return g_test_run();
+}

+ 1 - 0
tests/qtest/meson.build

@@ -21,6 +21,7 @@ qtests_generic = \
   (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \
   (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \
   (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \
+  (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \
   [
   'cdrom-test',
   'device-introspect-test',