Patchwork [for-2.0,14/47] vpc/vhd: add bounds check for max_table_entries and block_size (CVE-2014-0144)

login
register
mail settings
Submitter Stefan Hajnoczi
Date March 26, 2014, 12:05 p.m.
Message ID <1395835569-21193-15-git-send-email-stefanha@redhat.com>
Download mbox | patch
Permalink /patch/333942/
State New
Headers show

Comments

Stefan Hajnoczi - March 26, 2014, 12:05 p.m.
From: Jeff Cody <jcody@redhat.com>

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>
---
 block/vpc.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)
Max Reitz - March 26, 2014, 8:15 p.m.
On 26.03.2014 13:05, Stefan Hajnoczi wrote:
> From: Jeff Cody <jcody@redhat.com>
>
> 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>
> ---
>   block/vpc.c | 27 +++++++++++++++++++++++----
>   1 file changed, 23 insertions(+), 4 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>

Patch

diff --git a/block/vpc.c b/block/vpc.c
index 82bf248..ba82d48 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -45,6 +45,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
 typedef struct vhd_footer {
     char        creator[8]; // "conectix"
@@ -164,6 +166,7 @@  static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
     VHDDynDiskHeader *dyndisk_header;
     uint8_t buf[HEADER_SIZE];
     uint32_t checksum;
+    uint64_t computed_size;
     int disk_type = VHD_DYNAMIC;
     int ret;
 
@@ -222,7 +225,7 @@  static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     /* Allow a maximum disk size of approximately 2 TB */
-    if (bs->total_sectors >= 65535LL * 255 * 255) {
+    if (bs->total_sectors >= VHD_MAX_SECTORS) {
         ret = -EFBIG;
         goto fail;
     }
@@ -245,7 +248,23 @@  static int vpc_open(BlockDriverState *bs, QDict *options, 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) {
+            ret = -EINVAL;
+            goto fail;
+        }
+        if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) {
+            ret = -EINVAL;
+            goto fail;
+        }
+
+        computed_size = (uint64_t) s->max_table_entries * s->block_size;
+        if (computed_size < bs->total_sectors * 512) {
+            ret = -EINVAL;
+            goto fail;
+        }
+
+        s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4);
 
         s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
 
@@ -298,7 +317,7 @@  static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
     return 0;
 
 fail:
-    g_free(s->pagetable);
+    qemu_vfree(s->pagetable);
 #ifdef CACHE
     g_free(s->pageentry_u8);
 #endif
@@ -833,7 +852,7 @@  static int vpc_has_zero_init(BlockDriverState *bs)
 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