9ae3a8
From a5319d241b02f44f273b9283271b5017b45c0b50 Mon Sep 17 00:00:00 2001
9ae3a8
From: Jeffrey Cody <jcody@redhat.com>
9ae3a8
Date: Wed, 20 Nov 2013 19:43:58 +0100
9ae3a8
Subject: [PATCH 15/25] block: vhdx - add region overlap detection for image files
9ae3a8
9ae3a8
RH-Author: Jeffrey Cody <jcody@redhat.com>
9ae3a8
Message-id: <d98c86496e5956aa6bd9359f0f060982e1c44aa0.1384975172.git.jcody@redhat.com>
9ae3a8
Patchwork-id: 55808
9ae3a8
O-Subject: [RHEL7 qemu-kvm PATCH 15/26] block: vhdx - add region overlap detection for image files
9ae3a8
Bugzilla: 879234
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
9ae3a8
Regions in the image file cannot overlap - the log, region tables,
9ae3a8
and metdata must all be unique and non-overlapping.
9ae3a8
9ae3a8
This adds region checking by means of a QLIST; there can be a variable
9ae3a8
number of regions and metadata (there may be metadata or region tables
9ae3a8
that we do not recognize / know about, but are not required).
9ae3a8
9ae3a8
This adds the capability to register a region for later checking, and
9ae3a8
to check against registered regions for any overlap.
9ae3a8
9ae3a8
Also, if neither the BAT or Metadata region tables are found, return
9ae3a8
error.
9ae3a8
9ae3a8
Signed-off-by: Jeff Cody <jcody@redhat.com>
9ae3a8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
(cherry picked from commit 1a848fd4517820981b542e0d10e64c0426414229)
9ae3a8
---
9ae3a8
 block/vhdx.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/vhdx.h |  9 +++++++
9ae3a8
 2 files changed, 91 insertions(+)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/vhdx.c |   82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/vhdx.h |    9 ++++++
9ae3a8
 2 files changed, 91 insertions(+), 0 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/vhdx.c b/block/vhdx.c
9ae3a8
index b552dde..e36c60e 100644
9ae3a8
--- a/block/vhdx.c
9ae3a8
+++ b/block/vhdx.c
9ae3a8
@@ -204,6 +204,50 @@ void vhdx_guid_generate(MSGUID *guid)
9ae3a8
     memcpy(guid, uuid, sizeof(MSGUID));
9ae3a8
 }
9ae3a8
 
9ae3a8
+/* Check for region overlaps inside the VHDX image */
9ae3a8
+static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint64_t end;
9ae3a8
+    VHDXRegionEntry *r;
9ae3a8
+
9ae3a8
+    end = start + length;
9ae3a8
+    QLIST_FOREACH(r, &s->regions, entries) {
9ae3a8
+        if (!((start >= r->end) || (end <= r->start))) {
9ae3a8
+            ret = -EINVAL;
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Register a region for future checks */
9ae3a8
+static void vhdx_region_register(BDRVVHDXState *s,
9ae3a8
+                                 uint64_t start, uint64_t length)
9ae3a8
+{
9ae3a8
+    VHDXRegionEntry *r;
9ae3a8
+
9ae3a8
+    r = g_malloc0(sizeof(*r));
9ae3a8
+
9ae3a8
+    r->start = start;
9ae3a8
+    r->end = start + length;
9ae3a8
+
9ae3a8
+    QLIST_INSERT_HEAD(&s->regions, r, entries);
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Free all registered regions */
9ae3a8
+static void vhdx_region_unregister_all(BDRVVHDXState *s)
9ae3a8
+{
9ae3a8
+    VHDXRegionEntry *r, *r_next;
9ae3a8
+
9ae3a8
+    QLIST_FOREACH_SAFE(r, &s->regions, entries, r_next) {
9ae3a8
+        QLIST_REMOVE(r, entries);
9ae3a8
+        g_free(r);
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
 /*
9ae3a8
  * Per the MS VHDX Specification, for every VHDX file:
9ae3a8
  *      - The header section is fixed size - 1 MB
9ae3a8
@@ -389,6 +433,9 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
9ae3a8
         }
9ae3a8
     }
9ae3a8
 
9ae3a8
+    vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
9ae3a8
+                            s->headers[s->curr_header]->log_length);
9ae3a8
+
9ae3a8
     ret = 0;
9ae3a8
 
9ae3a8
     goto exit;
9ae3a8
@@ -452,6 +499,15 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
9ae3a8
         le32_to_cpus(&rt_entry.length);
9ae3a8
         le32_to_cpus(&rt_entry.data_bits);
9ae3a8
 
9ae3a8
+        /* check for region overlap between these entries, and any
9ae3a8
+         * other memory regions in the file */
9ae3a8
+        ret = vhdx_region_check(s, rt_entry.file_offset, rt_entry.length);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        vhdx_region_register(s, rt_entry.file_offset, rt_entry.length);
9ae3a8
+
9ae3a8
         /* see if we recognize the entry */
9ae3a8
         if (guid_eq(rt_entry.guid, bat_guid)) {
9ae3a8
             /* must be unique; if we have already found it this is invalid */
9ae3a8
@@ -482,6 +538,12 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
9ae3a8
             goto fail;
9ae3a8
         }
9ae3a8
     }
9ae3a8
+
9ae3a8
+    if (!bat_rt_found || !metadata_rt_found) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto fail;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     ret = 0;
9ae3a8
 
9ae3a8
 fail:
9ae3a8
@@ -751,6 +813,7 @@ static void vhdx_close(BlockDriverState *bs)
9ae3a8
     error_free(s->migration_blocker);
9ae3a8
     qemu_vfree(s->log.hdr);
9ae3a8
     s->log.hdr = NULL;
9ae3a8
+    vhdx_region_unregister_all(s);
9ae3a8
 }
9ae3a8
 
9ae3a8
 static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
@@ -768,6 +831,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
     s->first_visible_write = true;
9ae3a8
 
9ae3a8
     qemu_co_mutex_init(&s->lock);
9ae3a8
+    QLIST_INIT(&s->regions);
9ae3a8
 
9ae3a8
     /* validate the file signature */
9ae3a8
     ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t));
9ae3a8
@@ -842,8 +906,26 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    uint64_t payblocks = s->chunk_ratio;
9ae3a8
+    /* endian convert, and verify populated BAT field file offsets against
9ae3a8
+     * region table and log entries */
9ae3a8
     for (i = 0; i < s->bat_entries; i++) {
9ae3a8
         le64_to_cpus(&s->bat[i]);
9ae3a8
+        if (payblocks--) {
9ae3a8
+            /* payload bat entries */
9ae3a8
+            if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
9ae3a8
+                    PAYLOAD_BLOCK_FULL_PRESENT) {
9ae3a8
+                ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK,
9ae3a8
+                                        s->block_size);
9ae3a8
+                if (ret < 0) {
9ae3a8
+                    goto fail;
9ae3a8
+                }
9ae3a8
+            }
9ae3a8
+        } else {
9ae3a8
+            payblocks = s->chunk_ratio;
9ae3a8
+            /* Once differencing files are supported, verify sector bitmap
9ae3a8
+             * blocks here */
9ae3a8
+        }
9ae3a8
     }
9ae3a8
 
9ae3a8
     if (flags & BDRV_O_RDWR) {
9ae3a8
diff --git a/block/vhdx.h b/block/vhdx.h
9ae3a8
index b150ad1..91ef8fe 100644
9ae3a8
--- a/block/vhdx.h
9ae3a8
+++ b/block/vhdx.h
9ae3a8
@@ -230,6 +230,7 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
9ae3a8
    other bits are reserved */
9ae3a8
 #define VHDX_BAT_STATE_BIT_MASK 0x07
9ae3a8
 #define VHDX_BAT_FILE_OFF_BITS (64 - 44)
9ae3a8
+#define VHDX_BAT_FILE_OFF_MASK  0xFFFFFFFFFFF00000 /* upper 44 bits */
9ae3a8
 typedef uint64_t VHDXBatEntry;
9ae3a8
 
9ae3a8
 /* ---- METADATA REGION STRUCTURES ---- */
9ae3a8
@@ -334,6 +335,12 @@ typedef struct VHDXLogEntries {
9ae3a8
     uint32_t tail;
9ae3a8
 } VHDXLogEntries;
9ae3a8
 
9ae3a8
+typedef struct VHDXRegionEntry {
9ae3a8
+    uint64_t start;
9ae3a8
+    uint64_t end;
9ae3a8
+    QLIST_ENTRY(VHDXRegionEntry) entries;
9ae3a8
+} VHDXRegionEntry;
9ae3a8
+
9ae3a8
 typedef struct BDRVVHDXState {
9ae3a8
     CoMutex lock;
9ae3a8
 
9ae3a8
@@ -374,6 +381,8 @@ typedef struct BDRVVHDXState {
9ae3a8
     VHDXParentLocatorEntry *parent_entries;
9ae3a8
 
9ae3a8
     Error *migration_blocker;
9ae3a8
+
9ae3a8
+    QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions;
9ae3a8
 } BDRVVHDXState;
9ae3a8
 
9ae3a8
 void vhdx_guid_generate(MSGUID *guid);
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8