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