]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Fix CVE-2008-0928 - insufficient block device address range checking
authoraurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
Tue, 11 Mar 2008 17:17:59 +0000 (17:17 +0000)
committeraurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
Tue, 11 Mar 2008 17:17:59 +0000 (17:17 +0000)
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.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4037 c046a42c-6fe2-441c-8c8c-71466251a162

block-qcow.c
block-qcow2.c
block-vmdk.c
block.c
block.h
block_int.h

index 730ae67ed8f7095ca4d22e80aca57678be18fb6c..7de8a3f508d56efb287fbdff662ab155c12c5c94 100644 (file)
@@ -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))
index 577210b21acb6248443c88c1ad24141f83dcf3a9..30c13f376dde880424fce20d67bb731c4b7ce55c 100644 (file)
@@ -191,7 +191,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))
index 9b5fb7346a9f932cddfd194c15032ffb47a02063..79de09b67ed76e5b326bb716ee9c64fce02c92aa 100644 (file)
@@ -378,7 +378,7 @@ static int vmdk_open(BlockDriverState *bs, const char *filename, int flags)
         flags = BDRV_O_RDONLY;
     fprintf(stderr, "(VMDK) image open: flags=0x%x filename=%s\n", flags, bs->filename);
 
-    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/block.c b/block.c
index 07309541f319c3c4399bfededc3053904ed6640d..7836cd275e8d55c34e7de83dff236c552f0fa098 100644 (file)
--- a/block.c
+++ b/block.c
@@ -123,6 +123,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)
 {
@@ -335,6 +389,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;
@@ -379,6 +437,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
@@ -444,6 +503,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);
@@ -509,6 +569,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 (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
             memcpy(buf, bs->boot_sector_data, 512);
         sector_num++;
@@ -549,6 +611,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 (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
         memcpy(bs->boot_sector_data, buf, 512);
     }
@@ -674,6 +738,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);
 }
 
@@ -689,6 +755,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);
 }
 
@@ -955,6 +1023,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);
 }
 
@@ -1101,6 +1171,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;
 
     /* XXX: we assume that nb_sectors == 0 is suppored by the async read */
     if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
@@ -1132,6 +1204,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;
     if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
         memcpy(bs->boot_sector_data, buf, 512);
     }
diff --git a/block.h b/block.h
index b73050556abe3761c68e852d9bf425ba8e977de9..0673a635a2114a635f1b47eb59234ddb73d7b96b 100644 (file)
--- a/block.h
+++ b/block.h
@@ -45,6 +45,7 @@ typedef struct QEMUSnapshotInfo {
                                      it (default for
                                      bdrv_file_open()) */
 #define BDRV_O_DIRECT      0x0020
+#define BDRV_O_AUTOGROW    0x0040 /* Allow backing file to extend when writing past end of file */
 
 #ifndef QEMU_IMG
 void bdrv_info(void);
index 137000e14075fb2b1b2d55739a8a83a27c510e22..27e90b87e1f8a86300a5f493214b8a2427a95503 100644 (file)
@@ -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;