Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > aadbe78a25743146bb784eee19f007c5 > files > 14

kvm-83-164.el5_5.9.src.rpm

From d2f9839082c47048fb1153dcad1eab6c21884f2a Mon Sep 17 00:00:00 2001
From: Aurelien Jarno <aurel32>
Date: Thu, 2 Oct 2008 15:04:32 -0300
Subject: [PATCH 13/54] Fix CVE-2008-0928 - insufficient block device address range checking

Qemu 0.9.1 and earlier does not perform range checks for block device
read or write requests, which allows guest host users with root
privileges to access arbitrary memory and escape the virtual machine.

[ehabkost: Not included upstream yet because Fabrice disagrees with the
           approach used on this patch]

RH-Type: bugfix(security)
RH-Upstream-status: submitted(qemu-devel), rejected # refactoring needed
RH-Pointer: http://www.mail-archive.com/qemu-devel@nongnu.org/msg16397.html
RH-Pointer: https://bugzilla.redhat.com/show_bug.cgi?id=433560
---
 qemu/block-qcow.c  |    2 +-
 qemu/block-qcow2.c |    2 +-
 qemu/block-vmdk.c  |    2 +-
 qemu/block.c       |   74 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu/block.h       |    1 +
 qemu/block_int.h   |    1 +
 6 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/qemu/block-qcow.c b/qemu/block-qcow.c
index ab10e91..62f883c 100644
--- a/qemu/block-qcow.c
+++ b/qemu/block-qcow.c
@@ -95,7 +95,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
     int len, i, shift, ret;
     QCowHeader header;
 
-    ret = bdrv_file_open(&s->hd, filename, flags);
+    ret = bdrv_file_open(&s->hd, filename, flags | BDRV_O_AUTOGROW);
     if (ret < 0)
         return ret;
     if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header))
diff --git a/qemu/block-qcow2.c b/qemu/block-qcow2.c
index fa4dd83..f91ca31 100644
--- a/qemu/block-qcow2.c
+++ b/qemu/block-qcow2.c
@@ -197,7 +197,7 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
         flags |= BDRV_O_CACHE_WB;
         flags &= ~BDRV_O_CACHE_DEF;
     }
-    ret = bdrv_file_open(&s->hd, filename, flags);
+    ret = bdrv_file_open(&s->hd, filename, flags | BDRV_O_AUTOGROW);
     if (ret < 0)
         return ret;
     if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header))
diff --git a/qemu/block-vmdk.c b/qemu/block-vmdk.c
index cab2078..c64bd59 100644
--- a/qemu/block-vmdk.c
+++ b/qemu/block-vmdk.c
@@ -377,7 +377,7 @@ static int vmdk_open(BlockDriverState *bs, const char *filename, int flags)
         // Parent must be opened as RO.
         flags = BDRV_O_RDONLY;
 
-    ret = bdrv_file_open(&s->hd, filename, flags);
+    ret = bdrv_file_open(&s->hd, filename, flags | BDRV_O_AUTOGROW);
     if (ret < 0)
         return ret;
     if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic))
diff --git a/qemu/block.c b/qemu/block.c
index dc744dd..8627884 100644
--- a/qemu/block.c
+++ b/qemu/block.c
@@ -128,6 +128,60 @@ void path_combine(char *dest, int dest_size,
     }
 }
 
+static int bdrv_rd_badreq_sectors(BlockDriverState *bs,
+                                  int64_t sector_num, int nb_sectors)
+{
+    return
+        nb_sectors < 0 ||
+        sector_num < 0 ||
+        nb_sectors > bs->total_sectors ||
+        sector_num > bs->total_sectors - nb_sectors;
+}
+
+static int bdrv_rd_badreq_bytes(BlockDriverState *bs,
+                                int64_t offset, int count)
+{
+    int64_t size = bs->total_sectors << SECTOR_BITS;
+    return
+        count < 0 ||
+        size < 0 ||
+        count > size ||
+        offset > size - count;
+}
+
+static int bdrv_wr_badreq_sectors(BlockDriverState *bs,
+                                  int64_t sector_num, int nb_sectors)
+{
+    if (sector_num < 0 ||
+        nb_sectors < 0)
+        return 1;
+
+    if (sector_num > bs->total_sectors - nb_sectors) {
+        if (bs->autogrow)
+            bs->total_sectors = sector_num + nb_sectors;
+        else
+            return 1;
+    }
+    return 0;
+}
+
+static int bdrv_wr_badreq_bytes(BlockDriverState *bs,
+                                int64_t offset, int count)
+{
+    int64_t size = bs->total_sectors << SECTOR_BITS;
+    if (count < 0 ||
+        offset < 0)
+        return 1;
+
+    if (offset > size - count) {
+        if (bs->autogrow)
+            bs->total_sectors = (offset + count + SECTOR_SIZE - 1) >> SECTOR_BITS;
+        else
+            return 1;
+    }
+    return 0;
+}
+
 
 static void bdrv_register(BlockDriver *bdrv)
 {
@@ -340,6 +394,10 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
     bs->read_only = 0;
     bs->is_temporary = 0;
     bs->encrypted = 0;
+    bs->autogrow = 0;
+
+    if (flags & BDRV_O_AUTOGROW)
+        bs->autogrow = 1;
 
     if (flags & BDRV_O_SNAPSHOT) {
         BlockDriverState *bs1;
@@ -396,6 +454,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
     }
     bs->drv = drv;
     bs->opaque = qemu_mallocz(drv->instance_size);
+    bs->total_sectors = 0; /* driver will set if it does not do getlength */
     if (bs->opaque == NULL && drv->instance_size > 0)
         return -1;
     /* Note: for compatibility, we open disk image files as RDWR, and
@@ -461,6 +520,7 @@ void bdrv_close(BlockDriverState *bs)
         bs->drv = NULL;
 
         /* call the change callback */
+        bs->total_sectors = 0;
         bs->media_changed = 1;
         if (bs->change_cb)
             bs->change_cb(bs->change_opaque);
@@ -533,6 +593,8 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num,
     if (!drv)
         return -ENOMEDIUM;
 
+    if (bdrv_rd_badreq_sectors(bs, sector_num, nb_sectors))
+        return -EDOM;
     if (drv->bdrv_pread) {
         int ret, len;
         len = nb_sectors * 512;
@@ -565,6 +627,8 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
         return -ENOMEDIUM;
     if (bs->read_only)
         return -EACCES;
+    if (bdrv_wr_badreq_sectors(bs, sector_num, nb_sectors))
+        return -EDOM;
     if (drv->bdrv_pwrite) {
         int ret, len;
         len = nb_sectors * 512;
@@ -687,6 +751,8 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset,
         return -ENOMEDIUM;
     if (!drv->bdrv_pread)
         return bdrv_pread_em(bs, offset, buf1, count1);
+    if (bdrv_rd_badreq_bytes(bs, offset, count1))
+        return -EDOM;
     return drv->bdrv_pread(bs, offset, buf1, count1);
 }
 
@@ -702,6 +768,8 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
         return -ENOMEDIUM;
     if (!drv->bdrv_pwrite)
         return bdrv_pwrite_em(bs, offset, buf1, count1);
+    if (bdrv_wr_badreq_bytes(bs, offset, count1))
+        return -EDOM;
     return drv->bdrv_pwrite(bs, offset, buf1, count1);
 }
 
@@ -1127,6 +1195,8 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
         return -ENOMEDIUM;
     if (!drv->bdrv_write_compressed)
         return -ENOTSUP;
+    if (bdrv_wr_badreq_sectors(bs, sector_num, nb_sectors))
+        return -EDOM;
     return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
 }
 
@@ -1273,6 +1343,8 @@ BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
 
     if (!drv)
         return NULL;
+    if (bdrv_rd_badreq_sectors(bs, sector_num, nb_sectors))
+        return NULL;
 
     ret = drv->bdrv_aio_read(bs, sector_num, buf, nb_sectors, cb, opaque);
 
@@ -1296,6 +1368,8 @@ BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
         return NULL;
     if (bs->read_only)
         return NULL;
+    if (bdrv_wr_badreq_sectors(bs, sector_num, nb_sectors))
+        return NULL;
 
     ret = drv->bdrv_aio_write(bs, sector_num, buf, nb_sectors, cb, opaque);
 
diff --git a/qemu/block.h b/qemu/block.h
index c3314a1..5c09113 100644
--- a/qemu/block.h
+++ b/qemu/block.h
@@ -50,6 +50,7 @@ typedef struct QEMUSnapshotInfo {
 #define BDRV_O_NOCACHE     0x0020 /* do not use the host page cache */
 #define BDRV_O_CACHE_WB    0x0040 /* use write-back caching */
 #define BDRV_O_CACHE_DEF   0x0080 /* use default caching */
+#define BDRV_O_AUTOGROW    0x0100 /* Allow backing file to extend when writing past end of file */
 
 #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_CACHE_DEF)
 
diff --git a/qemu/block_int.h b/qemu/block_int.h
index 1fa660a..557e49e 100644
--- a/qemu/block_int.h
+++ b/qemu/block_int.h
@@ -97,6 +97,7 @@ struct BlockDriverState {
     int locked;    /* if true, the media cannot temporarily be ejected */
     int encrypted; /* if true, the media is encrypted */
     int sg;        /* if true, the device is a /dev/sg* */
+    int autogrow;  /* if true, the backing store can auto-extend to allocate new extents */
     /* event callback when inserting/removing */
     void (*change_cb)(void *opaque);
     void *change_opaque;
-- 
1.6.1