From 714a0140fd0061b44d63bd538a2d6a7de6f59be6 Mon Sep 17 00:00:00 2001 From: Jeff Cody <jcody@redhat.com> Date: Tue, 25 Mar 2014 14:23:22 +0100 Subject: [PATCH 15/49] vpc/vhd: add bounds check for max_table_entries and block_size (CVE-2014-0144) RH-Author: Kevin Wolf <kwolf@redhat.com> Message-id: <1395753835-7591-16-git-send-email-kwolf@redhat.com> Patchwork-id: n/a 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) Bugzilla: 1079455 RH-Acked-by: Jeff Cody <jcody@redhat.com> RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> From: Jeff Cody <jcody@redhat.com> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1079455 Upstream status: Embargoed 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 files changed, 23 insertions(+), 4 deletions(-) diff --git a/block/vpc.c b/block/vpc.c index 059069f..de5bc22 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 struct vhd_footer { char creator[8]; // "conectix" @@ -164,6 +166,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, struct vhd_dyndisk_header* dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; + uint64_t computed_size; int disk_type = VHD_DYNAMIC; int ret; @@ -212,7 +215,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; /* 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; } @@ -235,7 +238,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); @@ -281,7 +300,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 @@ -804,7 +823,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.1