]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/commitdiff
dmg: sanitize chunk length and sectorcount (CVE-2014-0145)
authorStefan Hajnoczi <stefanha@redhat.com>
Wed, 26 Mar 2014 12:05:58 +0000 (13:05 +0100)
committerStefan Hajnoczi <stefanha@redhat.com>
Tue, 1 Apr 2014 13:22:35 +0000 (15:22 +0200)
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>
block/dmg.c

index f98c94dc475a14d781105c0e9b636a13625a9843..ad253fe1ed36f2ae4532de4a914cc8a3cb61cb5b 100644 (file)
 #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];
                 }