qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/2] vdi: add bounds checks for block related header


From: Stefan Weil
Subject: [Qemu-devel] [PATCH 2/2] vdi: add bounds checks for block related header fields (CVE-2014-0144)
Date: Wed, 26 Mar 2014 22:38:05 +0100

(1) block_size must not be null.

(2) blocks_in_image * 4 must fit into a size_t.

(3) blocks_in_image * block_size must fit into a uint64_t.

Header field disk_size already has a bounds check which now works
because of modification (1) and (3).

This patch was inspired by Jeff Cody's patch for the same problem.

Cc: Jeff Cody <address@hidden>
Cc: Kevin Wolf <address@hidden>
Cc: Stefan Hajnoczi <address@hidden>
Signed-off-by: Stefan Weil <address@hidden>
---

Hello Stefan, hello Jeff,

I tried to improve your previous patch - maybe you want to improve it further
or take parts from it to fix that CVE (of which I have no information).

The patch was compiled on 32 and 64 bit Linux and cross compiled with MinGW-w64,
but not tested otherwise.

Regards
Stefan

 block/vdi.c |   50 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 5 deletions(-)

diff --git a/block/vdi.c b/block/vdi.c
index b832905..8fb59a1 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -420,7 +420,12 @@ static int vdi_open(BlockDriverState *bs, QDict *options, 
int flags,
                    header.sector_size, SECTOR_SIZE);
         ret = -ENOTSUP;
         goto fail;
-#if !defined(CONFIG_VDI_BLOCK_SIZE)
+#if defined(CONFIG_VDI_BLOCK_SIZE)
+    } else if (header.block_size == 0) {
+        error_setg(errp, "unsupported VDI image (block size is null)");
+        ret = -EINVAL;
+        goto fail;
+#else
     } else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
         error_setg(errp, "unsupported VDI image (block size %u is not %u)",
                    header.block_size, DEFAULT_CLUSTER_SIZE);
@@ -435,6 +440,18 @@ static int vdi_open(BlockDriverState *bs, QDict *options, 
int flags,
                    (uint64_t)header.blocks_in_image * header.block_size);
         ret = -ENOTSUP;
         goto fail;
+#if SIZE_MAX <= UINT32_MAX
+    } else if (header.blocks_in_image > SIZE_MAX / sizeof(uint32_t)) {
+        /* blocks_in_image * sizeof(uint32_t) must fit into size_t. */
+        error_setg(errp, "unsupported VDI image (number of blocks %u, "
+                   "only %zu are possible)",
+                   header.blocks_in_image, SIZE_MAX / sizeof(uint32_t));
+#endif
+    } else if (header.blocks_in_image > UINT64_MAX / header.block_size) {
+        /* blocks_in_image * block_size must fit into uint64_t. */
+        error_setg(errp, "unsupported VDI image (number of blocks %u, "
+                   "only %" PRIu64 " are possible)",
+                   header.blocks_in_image, UINT64_MAX / header.block_size);
     } else if (!uuid_is_null(header.uuid_link)) {
         error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
         ret = -ENOTSUP;
@@ -691,6 +708,33 @@ static int vdi_create(const char *filename, 
QEMUOptionParameter *options,
         options++;
     }
 
+#if defined(CONFIG_VDI_BLOCK_SIZE)
+    if (block_size == 0) {
+        return -EINVAL;
+    }
+#endif
+
+    if (bytes > UINT64_MAX - block_size) {
+        /* Overflow in calculation of blocks (see below). */
+        return -ENOTSUP;
+    }
+
+    /* We need enough blocks to store the given disk size,
+       so always round up. */
+    blocks = (bytes + block_size - 1) / block_size;
+
+#if SIZE_MAX <= UINT32_MAX
+    if (blocks > SIZE_MAX / sizeof(uint32_t)) {
+        /* blocks * sizeof(uint32_t) must fit into size_t. */
+        return -ENOTSUP;
+    }
+#endif
+
+    if (blocks > UINT64_MAX / block_size) {
+        /* blocks * block_size must fit into uint64_t. */
+        return -EINVAL;
+    }
+
     fd = qemu_open(filename,
                    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
                    0644);
@@ -698,10 +742,6 @@ static int vdi_create(const char *filename, 
QEMUOptionParameter *options,
         return -errno;
     }
 
-    /* We need enough blocks to store the given disk size,
-       so always round up. */
-    blocks = (bytes + block_size - 1) / block_size;
-
     bmap_size = blocks * sizeof(uint32_t);
     bmap_size = ((bmap_size + SECTOR_SIZE - 1) & ~(SECTOR_SIZE -1));
 
-- 
1.7.10.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]