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 -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl8NuSQACgkQ4+MsLN6t wN7MEg/+PER/n+CpmrC2lggQ3WJwNjvY09A4yfPfhKldjOi+25/amf/bQ2Zjmj7m HoiiPFu7vz+FugOfGv5YFlTS2+VNmN1UZqGqZRwY/YJJKg9am6TJ8zA4UBf4iegi OqNBJOPW/EYsAYdH3jUFmW15zAsRHEM6g2vZ1Z4WwVZqfYHsMB/y2khp9Fr+jGU0 6wDeG0cdap5QVsamIll4/BoxgBa5UdtBYjzo7QBENs+abvOf56jjUqZx0+AL/Ua/ IOpZ01mmPZJ4wJxPNT87gfEnHv0MRA7bSpJ7TAC80xVoQjeoK+V2Ohvy+rvYPaqm 5mR0l4M+GGhglCg44wV3uwNonmltCxvTgGqZrQPsa3WnXMFoXqwGZgwl6XrYdLzV hVODJAu/Ivegk9AAbVrZGXg/shQtkB4gyoOaE3Qoraf1az9/XudECIo+zwocP4Ip Z0ny8bwQKq2QGYrCU3NWlgWi30sj6PeW5e6Jgq/2b1sUeKuUgNuuBPcRmXQ6kaz5 vMX7qYsXAxvO7o1QlbASzdvSvOXGx+0J0CJctPnY4jAJ7qjvJTKOb0j+jwMNJy+D XFAgB+D0go+UvnaPJn6teIHzaD4NqWE37MaamxsMY6RWjAnoy1+OOvZIZTnq+LnH iLbgk2EsxlFyBd3aZ/51ukeTUxpNgu9J6iRcXB3yVNBS4vqlBDw= =VZsF -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
3a9163af4e
@ -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:
|
||||
|
||||
|
86
hw/sd/sd.c
86
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) {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user