From: Jeff Cody <jcody@redhat.com>
Date: Wed, 26 Mar 2014 13:05:36 +0100
Subject: vpc/vhd: add bounds check for max_table_entries and block_size (CVE-2014-0144)

This adds checks to make sure that max_table_entries and block_size
are in sane ranges.  Memory is allocated based on max_table_entries,
and block_size is used to calculate indices into that allocated
memory, so if these values are incorrect that can lead to potential
unbounded memory allocation, or invalid memory accesses.

Also, the allocation of the pagetable is changed from g_malloc0()
to qemu_blockalign().

Signed-off-by: Jeff Cody <jcody@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>
(cherry picked from commit 97f1c45c6f456572e5b504b8614e4a69e23b8e3a)
(parts - memleak fixing - cherry picked from commit 59294e465953ffb07d42dc61c827bb98cc0ca423)

Conflicts:
	block/vpc.c

diff --git a/block/vpc.c b/block/vpc.c
index 5cd13d1..f064b4a 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -42,6 +42,8 @@ enum vhd_type {
 // Seconds since Jan 1, 2000 0:00:00 (UTC)
 #define VHD_TIMESTAMP_BASE 946684800
 
+#define VHD_MAX_SECTORS       (65535LL * 255 * 255)
+
 // always big-endian
 struct vhd_footer {
     char        creator[8]; // "conectix"
@@ -160,6 +162,7 @@ static int vpc_open(BlockDriverState *bs, int flags)
     struct vhd_dyndisk_header* dyndisk_header;
     uint8_t buf[HEADER_SIZE];
     uint32_t checksum;
+    uint64_t computed_size;
     int err = -1;
     int disk_type = VHD_DYNAMIC;
 
@@ -198,7 +201,8 @@ static int vpc_open(BlockDriverState *bs, int flags)
     bs->total_sectors = (int64_t)
         be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
 
-    if (bs->total_sectors >= 65535 * 16 * 255) {
+    /* Allow a maximum disk size of approximately 2 TB */
+    if (bs->total_sectors >= VHD_MAX_SECTORS) {
         err = -EFBIG;
         goto fail;
     }
@@ -219,7 +223,23 @@ static int vpc_open(BlockDriverState *bs, int flags)
         s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
 
         s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
-        s->pagetable = g_malloc(s->max_table_entries * 4);
+
+        if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) {
+            err = -EINVAL;
+            goto fail;
+        }
+        if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) {
+            err = -EINVAL;
+            goto fail;
+        }
+
+        computed_size = (uint64_t) s->max_table_entries * s->block_size;
+        if (computed_size < bs->total_sectors * 512) {
+            err = -EINVAL;
+            goto fail;
+        }
+
+        s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4);
 
         s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
         if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
@@ -262,6 +282,10 @@ static int vpc_open(BlockDriverState *bs, int flags)
 
     return 0;
  fail:
+    qemu_vfree(s->pagetable);
+#ifdef CACHE
+    g_free(s->pageentry_u8);
+#endif
     return err;
 }
 
@@ -751,7 +775,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
 static void vpc_close(BlockDriverState *bs)
 {
     BDRVVPCState *s = bs->opaque;
-    g_free(s->pagetable);
+    qemu_vfree(s->pagetable);
 #ifdef CACHE
     g_free(s->pageentry_u8);
 #endif
-- 
1.7.10.4

