summaryrefslogtreecommitdiff
path: root/block/dmg.c
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2014-03-26 13:05:58 +0100
committerStefan Hajnoczi <stefanha@redhat.com>2014-04-01 15:22:35 +0200
commitc165f7758009a4f793c1fc19ebb69cf55313450b (patch)
tree1df17ab2f0ad8c601252cc2e363b7de4ec77e050 /block/dmg.c
parenteb71803b041f55779ea10d860c0f66df285c68de (diff)
downloadqemu-c165f7758009a4f793c1fc19ebb69cf55313450b.tar.gz
dmg: sanitize chunk length and sectorcount (CVE-2014-0145)
Chunk length and sectorcount are used for decompression buffers as well as the bdrv_pread() count argument. Ensure that they have reasonable values so neither memory allocation nor conversion from uint64_t to int will cause problems. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'block/dmg.c')
-rw-r--r--block/dmg.c24
1 files changed, 24 insertions, 0 deletions
diff --git a/block/dmg.c b/block/dmg.c
index f98c94dc47..ad253fe1ed 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -27,6 +27,14 @@
#include "qemu/module.h"
#include <zlib.h>
+enum {
+ /* Limit chunk sizes to prevent unreasonable amounts of memory being used
+ * or truncating when converting to 32-bit types
+ */
+ DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */
+ DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
+};
+
typedef struct BDRVDMGState {
CoMutex lock;
/* each chunk contains a certain number of sectors,
@@ -208,6 +216,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
}
offset += 8;
+ if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
+ error_report("sector count %" PRIu64 " for chunk %u is "
+ "larger than max (%u)",
+ s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
+ ret = -EINVAL;
+ goto fail;
+ }
+
ret = read_uint64(bs, offset, &s->offsets[i]);
if (ret < 0) {
goto fail;
@@ -221,6 +237,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
}
offset += 8;
+ if (s->lengths[i] > DMG_LENGTHS_MAX) {
+ error_report("length %" PRIu64 " for chunk %u is larger "
+ "than max (%u)",
+ s->lengths[i], i, DMG_LENGTHS_MAX);
+ ret = -EINVAL;
+ goto fail;
+ }
+
if (s->lengths[i] > max_compressed_size) {
max_compressed_size = s->lengths[i];
}