From 9157dd597d293ab7f599f4d96c3fe8a6e07c633d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 3 Jun 2020 19:59:16 +0200 Subject: hw/sd/sdcard: Restrict Class 6 commands to SCSD cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only SCSD cards support Class 6 (Block Oriented Write Protection) commands. "SD Specifications Part 1 Physical Layer Simplified Spec. v3.01" 4.3.14 Command Functional Difference in Card Capacity Types * Write Protected Group SDHC and SDXC do not support write-protected groups. Issuing CMD28, CMD29 and CMD30 generates the ILLEGAL_COMMAND error. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20200630133912.9428-7-f4bug@amsat.org> --- hw/sd/sd.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'hw/sd/sd.c') diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5137168d66..1cc16bfd31 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -920,6 +920,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->multi_blk_cnt = 0; } + if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + /* Only Standard Capacity cards support class 6 commands */ + return sd_illegal; + } + switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ case 0: /* CMD0: GO_IDLE_STATE */ -- cgit v1.2.3 From 6dd3a164f5b31c703c7d8372841ad3bd6a57de6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 5 Jun 2018 22:28:51 -0300 Subject: hw/sd/sdcard: Simplify realize() a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need to check if sd->blk is set twice. Reviewed-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20200630133912.9428-18-f4bug@amsat.org> --- hw/sd/sd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'hw/sd/sd.c') diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 1cc16bfd31..edd60a09c0 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2105,12 +2105,12 @@ static void sd_realize(DeviceState *dev, Error **errp) return; } - if (sd->blk && blk_is_read_only(sd->blk)) { - error_setg(errp, "Cannot use read-only drive as SD card"); - return; - } - if (sd->blk) { + if (blk_is_read_only(sd->blk)) { + error_setg(errp, "Cannot use read-only drive as SD card"); + return; + } + ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, errp); if (ret < 0) { -- cgit v1.2.3 From a9bcedd15a5834ca9ae6c3a97933e85ac7edbd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 7 Jul 2020 13:02:34 +0200 Subject: hw/sd/sdcard: Do not allow invalid SD card sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU allows to create SD card with unrealistic sizes. This could work, but some guests (at least Linux) consider sizes that are not a power of 2 as a firmware bug and fix the card size to the next power of 2. While the possibility to use small SD card images has been seen as a feature, it became a bug with CVE-2020-13253, where the guest is able to do OOB read/write accesses past the image size end. In a pair of commits we will fix CVE-2020-13253 as: Read command is rejected if BLOCK_LEN_ERROR or ADDRESS_ERROR occurred and no data transfer is performed. Write command is rejected if BLOCK_LEN_ERROR or ADDRESS_ERROR occurred and no data transfer is performed. WP_VIOLATION errors are not modified: the error bit is set, we stay in receive-data state, wait for a stop command. All further data transfer is ignored. See the check on sd->card_status at the beginning of sd_read_data() and sd_write_data(). While this is the correct behavior, in case QEMU create smaller SD cards, guests still try to access past the image size end, and QEMU considers this is an invalid address, thus "all further data transfer is ignored". This is wrong and make the guest looping until eventually timeouts. Fix by not allowing invalid SD card sizes (suggesting the expected size as a hint): $ qemu-system-arm -M orangepi-pc -drive file=rootfs.ext2,if=sd,format=raw qemu-system-arm: Invalid SD card size: 60 MiB SD card size has to be a power of 2, e.g. 64 MiB. You can resize disk images with 'qemu-img resize ' (note that this will lose data if you make the image smaller than it currently is). Cc: qemu-stable@nongnu.org Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Peter Maydell Message-Id: <20200713183209.26308-8-f4bug@amsat.org> --- hw/sd/sd.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'hw/sd/sd.c') diff --git a/hw/sd/sd.c b/hw/sd/sd.c index edd60a09c0..76d68359a4 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "hw/irq.h" #include "hw/registerfields.h" #include "sysemu/block-backend.h" @@ -2106,11 +2107,35 @@ static void sd_realize(DeviceState *dev, Error **errp) } if (sd->blk) { + int64_t blk_size; + if (blk_is_read_only(sd->blk)) { error_setg(errp, "Cannot use read-only drive as SD card"); return; } + blk_size = blk_getlength(sd->blk); + if (blk_size > 0 && !is_power_of_2(blk_size)) { + int64_t blk_size_aligned = pow2ceil(blk_size); + char *blk_size_str; + + blk_size_str = size_to_str(blk_size); + error_setg(errp, "Invalid SD card size: %s", blk_size_str); + g_free(blk_size_str); + + blk_size_str = size_to_str(blk_size_aligned); + error_append_hint(errp, + "SD card size has to be a power of 2, e.g. %s.\n" + "You can resize disk images with" + " 'qemu-img resize '\n" + "(note that this will lose data if you make the" + " image smaller than it currently is).\n", + blk_size_str); + g_free(blk_size_str); + + return; + } + ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, errp); if (ret < 0) { -- cgit v1.2.3 From 794d68de2f021a6d3874df41d6bbe8590ec05207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 13 Jul 2020 09:27:35 +0200 Subject: hw/sd/sdcard: Update coding style to make checkpatch.pl happy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make the next commit easier to review, clean this code first. Reviewed-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Reviewed-by: Alexander Bulekov Message-Id: <20200630133912.9428-3-f4bug@amsat.org> --- hw/sd/sd.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'hw/sd/sd.c') diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 76d68359a4..f4f76f8fd2 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1175,8 +1175,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->data_start = addr; sd->data_offset = 0; - if (sd->data_start + sd->blk_len > sd->size) + if (sd->data_start + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; + } return sd_r1; default: @@ -1191,8 +1192,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->data_start = addr; sd->data_offset = 0; - if (sd->data_start + sd->blk_len > sd->size) + if (sd->data_start + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; + } return sd_r1; default: @@ -1237,12 +1239,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->data_offset = 0; sd->blk_written = 0; - if (sd->data_start + sd->blk_len > sd->size) + if (sd->data_start + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; - if (sd_wp_addr(sd, sd->data_start)) + } + if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; - if (sd->csd[14] & 0x30) + } + if (sd->csd[14] & 0x30) { sd->card_status |= WP_VIOLATION; + } return sd_r1; default: @@ -1261,12 +1266,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->data_offset = 0; sd->blk_written = 0; - if (sd->data_start + sd->blk_len > sd->size) + if (sd->data_start + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; - if (sd_wp_addr(sd, sd->data_start)) + } + if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; - if (sd->csd[14] & 0x30) + } + if (sd->csd[14] & 0x30) { sd->card_status |= WP_VIOLATION; + } return sd_r1; default: -- cgit v1.2.3 From 790762e5487114341cccc5bffcec4cb3c022c3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 4 Jun 2020 19:22:29 +0200 Subject: hw/sd/sdcard: Do not switch to ReceivingData if address is invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only move the state machine to ReceivingData if there is no pending error. This avoids later OOB access while processing commands queued. "SD Specifications Part 1 Physical Layer Simplified Spec. v3.01" 4.3.3 Data Read Read command is rejected if BLOCK_LEN_ERROR or ADDRESS_ERROR occurred and no data transfer is performed. 4.3.4 Data Write Write command is rejected if BLOCK_LEN_ERROR or ADDRESS_ERROR occurred and no data transfer is performed. WP_VIOLATION errors are not modified: the error bit is set, we stay in receive-data state, wait for a stop command. All further data transfer is ignored. See the check on sd->card_status at the beginning of sd_read_data() and sd_write_data(). Fixes: CVE-2020-13253 Cc: qemu-stable@nongnu.org Reported-by: Alexander Bulekov Buglink: https://bugs.launchpad.net/qemu/+bug/1880822 Reviewed-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <20200630133912.9428-6-f4bug@amsat.org> --- hw/sd/sd.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'hw/sd/sd.c') diff --git a/hw/sd/sd.c b/hw/sd/sd.c index f4f76f8fd2..fad9cf1ee7 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1171,13 +1171,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 17: /* CMD17: READ_SINGLE_BLOCK */ switch (sd->state) { case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - if (sd->data_start + sd->blk_len > sd->size) { + if (addr + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; + return sd_r1; } + + sd->state = sd_sendingdata_state; + sd->data_start = addr; + sd->data_offset = 0; return sd_r1; default: @@ -1188,13 +1190,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 18: /* CMD18: READ_MULTIPLE_BLOCK */ switch (sd->state) { case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - if (sd->data_start + sd->blk_len > sd->size) { + if (addr + sd->blk_len > sd->size) { sd->card_status |= ADDRESS_ERROR; + return sd_r1; } + + sd->state = sd_sendingdata_state; + sd->data_start = addr; + sd->data_offset = 0; return sd_r1; default: @@ -1234,14 +1238,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Writing in SPI mode not implemented. */ if (sd->spi) break; + + if (addr + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + return sd_r1; + } + sd->state = sd_receivingdata_state; sd->data_start = addr; sd->data_offset = 0; sd->blk_written = 0; - if (sd->data_start + sd->blk_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; - } if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; } @@ -1261,14 +1268,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Writing in SPI mode not implemented. */ if (sd->spi) break; + + if (addr + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + return sd_r1; + } + sd->state = sd_receivingdata_state; sd->data_start = addr; sd->data_offset = 0; sd->blk_written = 0; - if (sd->data_start + sd->blk_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; - } if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; } -- cgit v1.2.3