Blame SOURCES/kvm-qcow2-Check-L1-L2-reftable-entries-for-alignment.patch

05bba0
From f48ffc234c0aa2017dc90c971bbb50d47362192b Mon Sep 17 00:00:00 2001
05bba0
From: Max Reitz <mreitz@redhat.com>
05bba0
Date: Sat, 13 Jun 2015 16:22:04 +0200
05bba0
Subject: [PATCH 10/42] qcow2: Check L1/L2/reftable entries for alignment
05bba0
05bba0
Message-id: <1434212556-3927-11-git-send-email-mreitz@redhat.com>
05bba0
Patchwork-id: 66029
05bba0
O-Subject: [RHEL-7.2 qemu-kvm PATCH 10/42] qcow2: Check L1/L2/reftable entries for alignment
05bba0
Bugzilla: 1129893
05bba0
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
05bba0
RH-Acked-by: Fam Zheng <famz@redhat.com>
05bba0
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
05bba0
05bba0
BZ: 1129893
05bba0
05bba0
Offsets taken from the L1, L2 and refcount tables are generally assumed
05bba0
to be correctly aligned. However, this cannot be guaranteed if the image
05bba0
has been written to by something different than qemu, thus check all
05bba0
offsets taken from these tables for correct cluster alignment.
05bba0
05bba0
Signed-off-by: Max Reitz <mreitz@redhat.com>
05bba0
Reviewed-by: Eric Blake <eblake@redhat.com>
05bba0
Message-id: 1409926039-29044-5-git-send-email-mreitz@redhat.com
05bba0
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
05bba0
(cherry picked from commit a97c67ee6c1546b985c1048c7a1f9e4fc13d9ee1)
05bba0
05bba0
Signed-off-by: Max Reitz <mreitz@redhat.com>
05bba0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
05bba0
---
05bba0
 block/qcow2-cluster.c  | 43 ++++++++++++++++++++++++++++++++++++++++---
05bba0
 block/qcow2-refcount.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
05bba0
 2 files changed, 82 insertions(+), 5 deletions(-)
05bba0
05bba0
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
05bba0
index f2f1170..0e3b8d7 100644
05bba0
--- a/block/qcow2-cluster.c
05bba0
+++ b/block/qcow2-cluster.c
05bba0
@@ -459,6 +459,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
05bba0
         goto out;
05bba0
     }
05bba0
 
05bba0
+    if (offset_into_cluster(s, l2_offset)) {
05bba0
+        qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
05bba0
+                                " unaligned (L1 index: %#" PRIx64 ")",
05bba0
+                                l2_offset, l1_index);
05bba0
+        return -EIO;
05bba0
+    }
05bba0
+
05bba0
     /* load the l2 table in memory */
05bba0
 
05bba0
     ret = l2_load(bs, l2_offset, &l2_table);
05bba0
@@ -481,8 +488,11 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
05bba0
         break;
05bba0
     case QCOW2_CLUSTER_ZERO:
05bba0
         if (s->qcow_version < 3) {
05bba0
-            qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
05bba0
-            return -EIO;
05bba0
+            qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
05bba0
+                                    " in pre-v3 image (L2 offset: %#" PRIx64
05bba0
+                                    ", L2 index: %#x)", l2_offset, l2_index);
05bba0
+            ret = -EIO;
05bba0
+            goto fail;
05bba0
         }
05bba0
         c = count_contiguous_clusters(nb_clusters, s->cluster_size,
05bba0
                 &l2_table[l2_index], 0,
05bba0
@@ -500,6 +510,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
05bba0
                 &l2_table[l2_index], 0,
05bba0
                 QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
05bba0
         *cluster_offset &= L2E_OFFSET_MASK;
05bba0
+        if (offset_into_cluster(s, *cluster_offset)) {
05bba0
+            qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
05bba0
+                                    PRIx64 " unaligned (L2 offset: %#" PRIx64
05bba0
+                                    ", L2 index: %#x)", *cluster_offset,
05bba0
+                                    l2_offset, l2_index);
05bba0
+            ret = -EIO;
05bba0
+            goto fail;
05bba0
+        }
05bba0
         break;
05bba0
     default:
05bba0
         abort();
05bba0
@@ -516,6 +534,10 @@ out:
05bba0
     *num = nb_available - index_in_cluster;
05bba0
 
05bba0
     return ret;
05bba0
+
05bba0
+fail:
05bba0
+    qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
05bba0
+    return ret;
05bba0
 }
05bba0
 
05bba0
 /*
05bba0
@@ -551,6 +573,12 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
05bba0
 
05bba0
     assert(l1_index < s->l1_size);
05bba0
     l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
05bba0
+    if (offset_into_cluster(s, l2_offset)) {
05bba0
+        qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
05bba0
+                                " unaligned (L1 index: %#" PRIx64 ")",
05bba0
+                                l2_offset, l1_index);
05bba0
+        return -EIO;
05bba0
+    }
05bba0
 
05bba0
     /* seek the l2 table of the given l2 offset */
05bba0
 
05bba0
@@ -918,6 +946,15 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
05bba0
         bool offset_matches =
05bba0
             (cluster_offset & L2E_OFFSET_MASK) == *host_offset;
05bba0
 
05bba0
+        if (offset_into_cluster(s, cluster_offset & L2E_OFFSET_MASK)) {
05bba0
+            qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset "
05bba0
+                                    "%#llx unaligned (guest offset: %#" PRIx64
05bba0
+                                    ")", cluster_offset & L2E_OFFSET_MASK,
05bba0
+                                    guest_offset);
05bba0
+            ret = -EIO;
05bba0
+            goto out;
05bba0
+        }
05bba0
+
05bba0
         if (*host_offset != 0 && !offset_matches) {
05bba0
             *bytes = 0;
05bba0
             ret = 0;
05bba0
@@ -949,7 +986,7 @@ out:
05bba0
 
05bba0
     /* Only return a host offset if we actually made progress. Otherwise we
05bba0
      * would make requirements for handle_alloc() that it can't fulfill */
05bba0
-    if (ret) {
05bba0
+    if (ret > 0) {
05bba0
         *host_offset = (cluster_offset & L2E_OFFSET_MASK)
05bba0
                      + offset_into_cluster(s, guest_offset);
05bba0
     }
05bba0
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
05bba0
index dbcc4c6..e66d593 100644
05bba0
--- a/block/qcow2-refcount.c
05bba0
+++ b/block/qcow2-refcount.c
05bba0
@@ -101,6 +101,13 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
05bba0
     if (!refcount_block_offset)
05bba0
         return 0;
05bba0
 
05bba0
+    if (offset_into_cluster(s, refcount_block_offset)) {
05bba0
+        qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64
05bba0
+                                " unaligned (reftable index: %#" PRIx64 ")",
05bba0
+                                refcount_block_offset, refcount_table_index);
05bba0
+        return -EIO;
05bba0
+    }
05bba0
+
05bba0
     ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
05bba0
         (void**) &refcount_block);
05bba0
     if (ret < 0) {
05bba0
@@ -174,6 +181,14 @@ static int alloc_refcount_block(BlockDriverState *bs,
05bba0
 
05bba0
         /* If it's already there, we're done */
05bba0
         if (refcount_block_offset) {
05bba0
+            if (offset_into_cluster(s, refcount_block_offset)) {
05bba0
+                qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
05bba0
+                                        PRIx64 " unaligned (reftable index: "
05bba0
+                                        "%#x)", refcount_block_offset,
05bba0
+                                        refcount_table_index);
05bba0
+                return -EIO;
05bba0
+            }
05bba0
+
05bba0
              return load_refcount_block(bs, refcount_block_offset,
05bba0
                  (void**) refcount_block);
05bba0
         }
05bba0
@@ -812,8 +827,14 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
05bba0
     case QCOW2_CLUSTER_NORMAL:
05bba0
     case QCOW2_CLUSTER_ZERO:
05bba0
         if (l2_entry & L2E_OFFSET_MASK) {
05bba0
-            qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
05bba0
-                                nb_clusters << s->cluster_bits, type);
05bba0
+            if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
05bba0
+                qcow2_signal_corruption(bs, false, -1, -1,
05bba0
+                                        "Cannot free unaligned cluster %#llx",
05bba0
+                                        l2_entry & L2E_OFFSET_MASK);
05bba0
+            } else {
05bba0
+                qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
05bba0
+                                    nb_clusters << s->cluster_bits, type);
05bba0
+            }
05bba0
         }
05bba0
         break;
05bba0
     case QCOW2_CLUSTER_UNALLOCATED:
05bba0
@@ -872,6 +893,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
05bba0
             old_l2_offset = l2_offset;
05bba0
             l2_offset &= L1E_OFFSET_MASK;
05bba0
 
05bba0
+            if (offset_into_cluster(s, l2_offset)) {
05bba0
+                qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#"
05bba0
+                                        PRIx64 " unaligned (L1 index: %#x)",
05bba0
+                                        l2_offset, i);
05bba0
+                ret = -EIO;
05bba0
+                goto fail;
05bba0
+            }
05bba0
+
05bba0
             ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
05bba0
                 (void**) &l2_table);
05bba0
             if (ret < 0) {
05bba0
@@ -905,6 +934,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
05bba0
 
05bba0
                     case QCOW2_CLUSTER_NORMAL:
05bba0
                     case QCOW2_CLUSTER_ZERO:
05bba0
+                        if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
05bba0
+                            qcow2_signal_corruption(bs, true, -1, -1, "Data "
05bba0
+                                                    "cluster offset %#llx "
05bba0
+                                                    "unaligned (L2 offset: %#"
05bba0
+                                                    PRIx64 ", L2 index: %#x)",
05bba0
+                                                    offset & L2E_OFFSET_MASK,
05bba0
+                                                    l2_offset, j);
05bba0
+                            ret = -EIO;
05bba0
+                            goto fail;
05bba0
+                        }
05bba0
+
05bba0
                         cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
05bba0
                         if (!cluster_index) {
05bba0
                             /* unallocated */
05bba0
-- 
05bba0
1.8.3.1
05bba0