提交 3a9163af 编写于 作者: P Peter Maydell

Merge remote-tracking branch...

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: NPeter Maydell <peter.maydell@linaro.org>
......@@ -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
......
......@@ -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:
......
......@@ -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) {
......
......@@ -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,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册