aboutsummaryrefslogtreecommitdiff
path: root/block/qcow.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-05-20 11:57:52 +0100
committerPeter Maydell <peter.maydell@linaro.org>2014-05-20 11:57:52 +0100
commitca8c0fab9504f000216308cafc833eb957fe9284 (patch)
tree5ee125a6112ff816903ac08f2c0de6d62c987061 /block/qcow.c
parentc5fa6c86d0765f837515d1c10654c621724a77e0 (diff)
parent465bee1da82e43f18d10c43cc7566d0284ad13a9 (diff)
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block patches # gpg: Signature made Mon 19 May 2014 15:21:14 BST using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: (22 commits) block: optimize zero writes with bdrv_write_zeroes blockdev: add a function to parse enum ids from strings util: add qemu_iovec_is_zero qcow1: Stricter backing file length check qcow1: Validate image size (CVE-2014-0223) qcow1: Validate L2 table size (CVE-2014-0222) qcow1: Check maximum cluster size qcow1: Make padding in the header explicit curl: Add usage documentation curl: Add sslverify option curl: Remove broken parsing of options from url curl: Fix build when curl_multi_socket_action isn't available qemu-iotests: Fix blkdebug in VM drive in 030 qemu-iotests: Fix core dump suppression in test 039 iotests: Add test for the JSON protocol block: Allow JSON filenames check-qdict: Add test for qdict_join() qdict: Add qdict_join() block: add test for vhdx image created by Disk2VHD block: vhdx - account for identical header sections ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'block/qcow.c')
-rw-r--r--block/qcow.c44
1 files changed, 37 insertions, 7 deletions
diff --git a/block/qcow.c b/block/qcow.c
index 937dd6dd1c..7fd57d744a 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -48,9 +48,10 @@ typedef struct QCowHeader {
uint64_t size; /* in bytes */
uint8_t cluster_bits;
uint8_t l2_bits;
+ uint16_t padding;
uint32_t crypt_method;
uint64_t l1_table_offset;
-} QCowHeader;
+} QEMU_PACKED QCowHeader;
#define L2_CACHE_SIZE 16
@@ -60,7 +61,7 @@ typedef struct BDRVQcowState {
int cluster_sectors;
int l2_bits;
int l2_size;
- int l1_size;
+ unsigned int l1_size;
uint64_t cluster_offset_mask;
uint64_t l1_table_offset;
uint64_t *l1_table;
@@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVQcowState *s = bs->opaque;
- int len, i, shift, ret;
+ unsigned int len, i, shift;
+ int ret;
QCowHeader header;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
@@ -127,11 +129,25 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- if (header.size <= 1 || header.cluster_bits < 9) {
- error_setg(errp, "invalid value in qcow header");
+ if (header.size <= 1) {
+ error_setg(errp, "Image size is too small (must be at least 2 bytes)");
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (header.cluster_bits < 9 || header.cluster_bits > 16) {
+ error_setg(errp, "Cluster size must be between 512 and 64k");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* l2_bits specifies number of entries; storing a uint64_t in each entry,
+ * so bytes = num_entries << 3. */
+ if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) {
+ error_setg(errp, "L2 table size must be between 512 and 64k");
ret = -EINVAL;
goto fail;
}
+
if (header.crypt_method > QCOW_CRYPT_AES) {
error_setg(errp, "invalid encryption method in qcow header");
ret = -EINVAL;
@@ -151,7 +167,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
/* read the level 1 table */
shift = s->cluster_bits + s->l2_bits;
- s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
+ if (header.size > UINT64_MAX - (1LL << shift)) {
+ error_setg(errp, "Image too large");
+ ret = -EINVAL;
+ goto fail;
+ } else {
+ uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift;
+ if (l1_size > INT_MAX / sizeof(uint64_t)) {
+ error_setg(errp, "Image too large");
+ ret = -EINVAL;
+ goto fail;
+ }
+ s->l1_size = l1_size;
+ }
s->l1_table_offset = header.l1_table_offset;
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
@@ -175,7 +203,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
if (header.backing_file_offset != 0) {
len = header.backing_file_size;
if (len > 1023) {
- len = 1023;
+ error_setg(errp, "Backing file name too long");
+ ret = -EINVAL;
+ goto fail;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
bs->backing_file, len);