0a122b
From 714a0140fd0061b44d63bd538a2d6a7de6f59be6 Mon Sep 17 00:00:00 2001
0a122b
From: Jeff Cody <jcody@redhat.com>
0a122b
Date: Tue, 25 Mar 2014 14:23:22 +0100
0a122b
Subject: [PATCH 15/49] vpc/vhd: add bounds check for max_table_entries and block_size (CVE-2014-0144)
0a122b
0a122b
RH-Author: Kevin Wolf <kwolf@redhat.com>
0a122b
Message-id: <1395753835-7591-16-git-send-email-kwolf@redhat.com>
0a122b
Patchwork-id: n/a
0a122b
O-Subject: [virt-devel] [EMBARGOED RHEL-7.0 qemu-kvm PATCH 15/48] vpc/vhd: add bounds check for max_table_entries and block_size (CVE-2014-0144)
0a122b
Bugzilla: 1079455
0a122b
RH-Acked-by: Jeff Cody <jcody@redhat.com>
0a122b
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
0a122b
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
0a122b
0a122b
From: Jeff Cody <jcody@redhat.com>
0a122b
0a122b
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1079455
0a122b
Upstream status: Embargoed
0a122b
0a122b
This adds checks to make sure that max_table_entries and block_size
0a122b
are in sane ranges. Memory is allocated based on max_table_entries,
0a122b
and block_size is used to calculate indices into that allocated
0a122b
memory, so if these values are incorrect that can lead to potential
0a122b
unbounded memory allocation, or invalid memory accesses.
0a122b
0a122b
Also, the allocation of the pagetable is changed from g_malloc0()
0a122b
to qemu_blockalign().
0a122b
0a122b
Signed-off-by: Jeff Cody <jcody@redhat.com>
0a122b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
0a122b
---
0a122b
 block/vpc.c |   27 +++++++++++++++++++++++----
0a122b
 1 files changed, 23 insertions(+), 4 deletions(-)
0a122b
0a122b
diff --git a/block/vpc.c b/block/vpc.c
0a122b
index 059069f..de5bc22 100644
0a122b
--- a/block/vpc.c
0a122b
+++ b/block/vpc.c
0a122b
@@ -45,6 +45,8 @@ enum vhd_type {
0a122b
 // Seconds since Jan 1, 2000 0:00:00 (UTC)
0a122b
 #define VHD_TIMESTAMP_BASE 946684800
0a122b
 
0a122b
+#define VHD_MAX_SECTORS       (65535LL * 255 * 255)
0a122b
+
0a122b
 // always big-endian
0a122b
 struct vhd_footer {
0a122b
     char        creator[8]; // "conectix"
0a122b
@@ -164,6 +166,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
0a122b
     struct vhd_dyndisk_header* dyndisk_header;
0a122b
     uint8_t buf[HEADER_SIZE];
0a122b
     uint32_t checksum;
0a122b
+    uint64_t computed_size;
0a122b
     int disk_type = VHD_DYNAMIC;
0a122b
     int ret;
0a122b
 
0a122b
@@ -212,7 +215,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
0a122b
         be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
0a122b
 
0a122b
     /* Allow a maximum disk size of approximately 2 TB */
0a122b
-    if (bs->total_sectors >= 65535LL * 255 * 255) {
0a122b
+    if (bs->total_sectors >= VHD_MAX_SECTORS) {
0a122b
         ret = -EFBIG;
0a122b
         goto fail;
0a122b
     }
0a122b
@@ -235,7 +238,23 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
0a122b
         s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
0a122b
 
0a122b
         s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
0a122b
-        s->pagetable = g_malloc(s->max_table_entries * 4);
0a122b
+
0a122b
+        if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) {
0a122b
+            ret = -EINVAL;
0a122b
+            goto fail;
0a122b
+        }
0a122b
+        if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) {
0a122b
+            ret = -EINVAL;
0a122b
+            goto fail;
0a122b
+        }
0a122b
+
0a122b
+        computed_size = (uint64_t) s->max_table_entries * s->block_size;
0a122b
+        if (computed_size < bs->total_sectors * 512) {
0a122b
+            ret = -EINVAL;
0a122b
+            goto fail;
0a122b
+        }
0a122b
+
0a122b
+        s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4);
0a122b
 
0a122b
         s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
0a122b
 
0a122b
@@ -281,7 +300,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
0a122b
     return 0;
0a122b
 
0a122b
 fail:
0a122b
-    g_free(s->pagetable);
0a122b
+    qemu_vfree(s->pagetable);
0a122b
 #ifdef CACHE
0a122b
     g_free(s->pageentry_u8);
0a122b
 #endif
0a122b
@@ -804,7 +823,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options,
0a122b
 static void vpc_close(BlockDriverState *bs)
0a122b
 {
0a122b
     BDRVVPCState *s = bs->opaque;
0a122b
-    g_free(s->pagetable);
0a122b
+    qemu_vfree(s->pagetable);
0a122b
 #ifdef CACHE
0a122b
     g_free(s->pageentry_u8);
0a122b
 #endif
0a122b
-- 
0a122b
1.7.1
0a122b