aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-07-15 09:06:55 +0100
committerPeter Maydell <peter.maydell@linaro.org>2020-07-15 09:06:55 +0100
commit3a9163af4e3dd61795a35d47b702e302f98f81d6 (patch)
treeb88eed6621df1f1f23c1f1233a9c8e1b0a3c72ff
parentc920fdba39480989cb5f1af3cc63acccef021b54 (diff)
parent790762e5487114341cccc5bffcec4cb3c022c3cd (diff)
downloadqemu-3a9163af4e3dd61795a35d47b702e302f98f81d6.zip
qemu-3a9163af4e3dd61795a35d47b702e302f98f81d6.tar.gz
qemu-3a9163af4e3dd61795a35d47b702e302f98f81d6.tar.bz2
Merge remote-tracking branch 'remotes/philmd-gitlab/tags/sdcard-CVE-2020-13253-pull-request' into staging
Fix CVE-2020-13253 By using invalidated address, guest can do out-of-bounds accesses. These patches fix the issue by only allowing SD card image sizes power of 2, and not switching to SEND_DATA state when the address is invalid (out of range). This issue was found using QEMU fuzzing mode (using --enable-fuzzing, see docs/devel/fuzzing.txt) and reported by Alexander Bulekov. Reproducer: https://bugs.launchpad.net/qemu/+bug/1880822/comments/1 CI jobs results: . https://cirrus-ci.com/build/5157142548185088 . https://gitlab.com/philmd/qemu/-/pipelines/166381731 . https://travis-ci.org/github/philmd/qemu/builds/707956535 # gpg: Signature made Tue 14 Jul 2020 14:54:44 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-gitlab/tags/sdcard-CVE-2020-13253-pull-request: hw/sd/sdcard: Do not switch to ReceivingData if address is invalid hw/sd/sdcard: Update coding style to make checkpatch.pl happy hw/sd/sdcard: Do not allow invalid SD card sizes hw/sd/sdcard: Simplify realize() a bit hw/sd/sdcard: Restrict Class 6 commands to SCSD cards tests/acceptance/boot_linux: Expand SD card image to power of 2 tests/acceptance/boot_linux: Tag tests using a SD card with 'device:sd' docs/orangepi: Add instructions for resizing SD image to power of two MAINTAINERS: Cc qemu-block mailing list Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS1
-rw-r--r--docs/system/arm/orangepi.rst16
-rw-r--r--hw/sd/sd.c86
-rw-r--r--tests/acceptance/boot_linux_console.py34
4 files changed, 106 insertions, 31 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 80fa883..310082f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1674,6 +1674,7 @@ F: hw/ssi/xilinx_*
SD (Secure Card)
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+L: qemu-block@nongnu.org
S: Odd Fixes
F: include/hw/sd/sd*
F: hw/sd/core.c
diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst
index c41adad..6f23907 100644
--- a/docs/system/arm/orangepi.rst
+++ b/docs/system/arm/orangepi.rst
@@ -127,6 +127,16 @@ can be downloaded from:
Alternatively, you can also choose to build you own image with buildroot
using the orangepi_pc_defconfig. Also see https://buildroot.org for more information.
+When using an image as an SD card, it must be resized to a power of two. This can be
+done with the qemu-img command. It is recommended to only increase the image size
+instead of shrinking it to a power of two, to avoid loss of data. For example,
+to prepare a downloaded Armbian image, first extract it and then increase
+its size to one gigabyte as follows:
+
+.. code-block:: bash
+
+ $ qemu-img resize Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img 1G
+
You can choose to attach the selected image either as an SD card or as USB mass storage.
For example, to boot using the Orange Pi PC Debian image on SD card, simply add the -sd
argument and provide the proper root= kernel parameter:
@@ -213,12 +223,12 @@ Next, unzip the NetBSD image and write the U-Boot binary including SPL using:
$ dd if=/path/to/u-boot-sunxi-with-spl.bin of=armv7.img bs=1024 seek=8 conv=notrunc
Finally, before starting the machine the SD image must be extended such
-that the NetBSD kernel will not conclude the NetBSD partition is larger than
-the emulated SD card:
+that the size of the SD image is a power of two and that the NetBSD kernel
+will not conclude the NetBSD partition is larger than the emulated SD card:
.. code-block:: bash
- $ dd if=/dev/zero bs=1M count=64 >> armv7.img
+ $ qemu-img resize armv7.img 2G
Start the machine using the following command:
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 5137168..fad9cf1 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"
@@ -920,6 +921,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 */
@@ -1165,12 +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:
+
+ 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;
-
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
return sd_r1;
default:
@@ -1181,12 +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:
+
+ 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;
-
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
return sd_r1;
default:
@@ -1226,17 +1238,23 @@ 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))
+ 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:
@@ -1250,17 +1268,23 @@ 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))
+ 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:
@@ -2100,12 +2124,36 @@ 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) {
+ 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 <imagefile> <new-size>'\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) {
diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index 3d02519..67c3b2f 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -28,6 +28,22 @@ try:
except CmdNotFoundError:
P7ZIP_AVAILABLE = False
+"""
+Round up to next power of 2
+"""
+def pow2ceil(x):
+ return 1 if x == 0 else 2**(x - 1).bit_length()
+
+"""
+Expand file size to next power of 2
+"""
+def image_pow2ceil_expand(path):
+ size = os.path.getsize(path)
+ size_aligned = pow2ceil(size)
+ if size != size_aligned:
+ with open(path, 'ab+') as fd:
+ fd.truncate(size_aligned)
+
class LinuxKernelTest(Test):
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
@@ -620,6 +636,7 @@ class BootLinuxConsole(LinuxKernelTest):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb')
@@ -635,6 +652,7 @@ class BootLinuxConsole(LinuxKernelTest):
rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
archive.lzma_uncompress(rootfs_path_xz, rootfs_path)
+ image_pow2ceil_expand(rootfs_path)
self.vm.set_console()
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
@@ -669,9 +687,10 @@ class BootLinuxConsole(LinuxKernelTest):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
- # This test download a 196MB compressed image and expand it to 932MB...
+ # This test download a 196MB compressed image and expand it to 1GB
image_url = ('https://dl.armbian.com/orangepipc/archive/'
'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.7z')
image_hash = '196a8ffb72b0123d92cea4a070894813d305c71e'
@@ -679,6 +698,7 @@ class BootLinuxConsole(LinuxKernelTest):
image_name = 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img'
image_path = os.path.join(self.workdir, image_name)
process.run("7z e -o%s %s" % (self.workdir, image_path_7z))
+ image_pow2ceil_expand(image_path)
self.vm.set_console()
self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
@@ -710,8 +730,9 @@ class BootLinuxConsole(LinuxKernelTest):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
- # This test download a 304MB compressed image and expand it to 1.3GB...
+ # This test download a 304MB compressed image and expand it to 2GB
deb_url = ('http://snapshot.debian.org/archive/debian/'
'20200108T145233Z/pool/main/u/u-boot/'
'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb')
@@ -728,8 +749,9 @@ class BootLinuxConsole(LinuxKernelTest):
image_hash = '2babb29d36d8360adcb39c09e31060945259917a'
image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
image_path = os.path.join(self.workdir, 'armv7.img')
- image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
archive.gzip_uncompress(image_path_gz, image_path)
+ image_pow2ceil_expand(image_path)
+ image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
# dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc
with open(uboot_path, 'rb') as f_in:
@@ -737,12 +759,6 @@ class BootLinuxConsole(LinuxKernelTest):
f_out.seek(8 * 1024)
shutil.copyfileobj(f_in, f_out)
- # Extend image, to avoid that NetBSD thinks the partition
- # inside the image is larger than device size itself
- f_out.seek(0, 2)
- f_out.seek(64 * 1024 * 1024, 1)
- f_out.write(bytearray([0x00]))
-
self.vm.set_console()
self.vm.add_args('-nic', 'user',
'-drive', image_drive_args,