From f89bf2bcc65af01dffeb83e54715770d883488a1 Mon Sep 17 00:00:00 2001
From: Jeff Cody <jcody@redhat.com>
Date: Tue, 25 Mar 2014 11:45:51 +0100
Subject: [PATCH 33/48] vdi: add bounds checks for blocks_in_image and disk_size header fields (CVE-2014-0144)

RH-Author: Kevin Wolf <kwolf@redhat.com>
Message-id: <1395744364-16049-33-git-send-email-kwolf@redhat.com>
Patchwork-id: n/a
O-Subject: [EMBARGOED RHEL-6.6/6.5.z qemu-kvm PATCH v2 32/45]
           vdi: add bounds checks for blocks_in_image and disk_size
           header fields (CVE-2014-0144)
Bugzilla: 1079453
RH-Acked-by: Max Reitz <mreitz@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Jeff Cody <jcody@redhat.com>

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1079453
Upstream status: Embargoed

The maximum blocks_in_image is 0xffffffff / 4, which also limits the
maximum disk_size for a VDI image.

Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>

Conflicts:
	block/vdi.c

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/vdi.c |   27 ++++++++++++++++++++++++---
 1 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/block/vdi.c b/block/vdi.c
index eaa4de1..b6aff93 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -114,6 +114,12 @@ typedef unsigned char uuid_t[16];
 /* Unallocated blocks use this index (no need to convert endianess). */
 #define VDI_UNALLOCATED UINT32_MAX
 
+#define VDI_BLOCK_SIZE           (1 * MiB)
+/* max blocks in image is (0xffffffff / 4) */
+#define VDI_BLOCKS_IN_IMAGE_MAX  0x3fffffff
+#define VDI_DISK_SIZE_MAX        ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
+                                  (uint64_t)VDI_BLOCK_SIZE)
+
 #if !defined(CONFIG_UUID)
 static inline void uuid_generate(uuid_t out)
 {
@@ -395,6 +401,10 @@ static int vdi_open(BlockDriverState *bs, int flags)
     vdi_header_print(&header);
 #endif
 
+    if (header.disk_size > VDI_DISK_SIZE_MAX) {
+        goto fail;
+    }
+
     if (header.version != VDI_VERSION_1_1) {
         logout("unsupported version %u.%u\n",
                header.version >> 16, header.version & 0xffff);
@@ -410,8 +420,9 @@ static int vdi_open(BlockDriverState *bs, int flags)
     } else if (header.sector_size != SECTOR_SIZE) {
         logout("unsupported sector size %u B\n", header.sector_size);
         goto fail;
-    } else if (header.block_size != 1 * MiB) {
-        logout("unsupported block size %u B\n", header.block_size);
+    } else if (header.block_size != VDI_BLOCK_SIZE) {
+        logout("unsupported VDI image (sector size %u is not %u)",
+               header.block_size, VDI_BLOCK_SIZE);
         goto fail;
     } else if (header.disk_size !=
                (uint64_t)header.blocks_in_image * header.block_size) {
@@ -423,6 +434,9 @@ static int vdi_open(BlockDriverState *bs, int flags)
     } else if (!uuid_is_null(header.uuid_parent)) {
         logout("parent uuid != 0, unsupported\n");
         goto fail;
+    } else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) {
+        logout("unsupported number of blocks in image\n");
+        goto fail;
     }
 
     bs->total_sectors = header.disk_size / SECTOR_SIZE;
@@ -835,10 +849,16 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
         options++;
     }
 
+    if (bytes > VDI_DISK_SIZE_MAX) {
+        result = -EINVAL;
+        goto exit;
+    }
+
     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
               0644);
     if (fd < 0) {
-        return -errno;
+        result = -errno;
+        goto exit;
     }
 
     blocks = bytes / block_size;
@@ -893,6 +913,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
         result = -errno;
     }
 
+exit:
     return result;
 }
 
-- 
1.7.1