qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Hajnoczi <stefanha@redhat.com>
To: qemu-devel@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	Stefan Hajnoczi <stefanha@redhat.com>,
	Max Reitz <mreitz@redhat.com>
Subject: [Qemu-devel] [PULL 34/59] qcow2: Check L1/L2/reftable entries for alignment
Date: Fri, 19 Sep 2014 15:41:53 +0100	[thread overview]
Message-ID: <1411137738-31280-35-git-send-email-stefanha@redhat.com> (raw)
In-Reply-To: <1411137738-31280-1-git-send-email-stefanha@redhat.com>

From: Max Reitz <mreitz@redhat.com>

Offsets taken from the L1, L2 and refcount tables are generally assumed
to be correctly aligned. However, this cannot be guaranteed if the image
has been written to by something different than qemu, thus check all
offsets taken from these tables for correct cluster alignment.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1409926039-29044-5-git-send-email-mreitz@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/qcow2-cluster.c  | 43 ++++++++++++++++++++++++++++++++++++++++---
 block/qcow2-refcount.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 735f687..f7dd8c0 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -486,6 +486,13 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
         goto out;
     }
 
+    if (offset_into_cluster(s, l2_offset)) {
+        qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
+                                " unaligned (L1 index: %#" PRIx64 ")",
+                                l2_offset, l1_index);
+        return -EIO;
+    }
+
     /* load the l2 table in memory */
 
     ret = l2_load(bs, l2_offset, &l2_table);
@@ -508,8 +515,11 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
         break;
     case QCOW2_CLUSTER_ZERO:
         if (s->qcow_version < 3) {
-            qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
-            return -EIO;
+            qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found"
+                                    " in pre-v3 image (L2 offset: %#" PRIx64
+                                    ", L2 index: %#x)", l2_offset, l2_index);
+            ret = -EIO;
+            goto fail;
         }
         c = count_contiguous_clusters(nb_clusters, s->cluster_size,
                 &l2_table[l2_index], QCOW_OFLAG_ZERO);
@@ -525,6 +535,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
         c = count_contiguous_clusters(nb_clusters, s->cluster_size,
                 &l2_table[l2_index], QCOW_OFLAG_ZERO);
         *cluster_offset &= L2E_OFFSET_MASK;
+        if (offset_into_cluster(s, *cluster_offset)) {
+            qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset %#"
+                                    PRIx64 " unaligned (L2 offset: %#" PRIx64
+                                    ", L2 index: %#x)", *cluster_offset,
+                                    l2_offset, l2_index);
+            ret = -EIO;
+            goto fail;
+        }
         break;
     default:
         abort();
@@ -541,6 +559,10 @@ out:
     *num = nb_available - index_in_cluster;
 
     return ret;
+
+fail:
+    qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
+    return ret;
 }
 
 /*
@@ -576,6 +598,12 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
 
     assert(l1_index < s->l1_size);
     l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
+    if (offset_into_cluster(s, l2_offset)) {
+        qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64
+                                " unaligned (L1 index: %#" PRIx64 ")",
+                                l2_offset, l1_index);
+        return -EIO;
+    }
 
     /* seek the l2 table of the given l2 offset */
 
@@ -948,6 +976,15 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
         bool offset_matches =
             (cluster_offset & L2E_OFFSET_MASK) == *host_offset;
 
+        if (offset_into_cluster(s, cluster_offset & L2E_OFFSET_MASK)) {
+            qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset "
+                                    "%#llx unaligned (guest offset: %#" PRIx64
+                                    ")", cluster_offset & L2E_OFFSET_MASK,
+                                    guest_offset);
+            ret = -EIO;
+            goto out;
+        }
+
         if (*host_offset != 0 && !offset_matches) {
             *bytes = 0;
             ret = 0;
@@ -979,7 +1016,7 @@ out:
 
     /* Only return a host offset if we actually made progress. Otherwise we
      * would make requirements for handle_alloc() that it can't fulfill */
-    if (ret) {
+    if (ret > 0) {
         *host_offset = (cluster_offset & L2E_OFFSET_MASK)
                      + offset_into_cluster(s, guest_offset);
     }
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index b9d421e..2bcaaf9 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -108,6 +108,13 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
     if (!refcount_block_offset)
         return 0;
 
+    if (offset_into_cluster(s, refcount_block_offset)) {
+        qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64
+                                " unaligned (reftable index: %#" PRIx64 ")",
+                                refcount_block_offset, refcount_table_index);
+        return -EIO;
+    }
+
     ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
         (void**) &refcount_block);
     if (ret < 0) {
@@ -181,6 +188,14 @@ static int alloc_refcount_block(BlockDriverState *bs,
 
         /* If it's already there, we're done */
         if (refcount_block_offset) {
+            if (offset_into_cluster(s, refcount_block_offset)) {
+                qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
+                                        PRIx64 " unaligned (reftable index: "
+                                        "%#x)", refcount_block_offset,
+                                        refcount_table_index);
+                return -EIO;
+            }
+
              return load_refcount_block(bs, refcount_block_offset,
                  (void**) refcount_block);
         }
@@ -836,8 +851,14 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
     case QCOW2_CLUSTER_NORMAL:
     case QCOW2_CLUSTER_ZERO:
         if (l2_entry & L2E_OFFSET_MASK) {
-            qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
-                                nb_clusters << s->cluster_bits, type);
+            if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) {
+                qcow2_signal_corruption(bs, false, -1, -1,
+                                        "Cannot free unaligned cluster %#llx",
+                                        l2_entry & L2E_OFFSET_MASK);
+            } else {
+                qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
+                                    nb_clusters << s->cluster_bits, type);
+            }
         }
         break;
     case QCOW2_CLUSTER_UNALLOCATED:
@@ -901,6 +922,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
             old_l2_offset = l2_offset;
             l2_offset &= L1E_OFFSET_MASK;
 
+            if (offset_into_cluster(s, l2_offset)) {
+                qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#"
+                                        PRIx64 " unaligned (L1 index: %#x)",
+                                        l2_offset, i);
+                ret = -EIO;
+                goto fail;
+            }
+
             ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
                 (void**) &l2_table);
             if (ret < 0) {
@@ -933,6 +962,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
 
                     case QCOW2_CLUSTER_NORMAL:
                     case QCOW2_CLUSTER_ZERO:
+                        if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {
+                            qcow2_signal_corruption(bs, true, -1, -1, "Data "
+                                                    "cluster offset %#llx "
+                                                    "unaligned (L2 offset: %#"
+                                                    PRIx64 ", L2 index: %#x)",
+                                                    offset & L2E_OFFSET_MASK,
+                                                    l2_offset, j);
+                            ret = -EIO;
+                            goto fail;
+                        }
+
                         cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
                         if (!cluster_index) {
                             /* unallocated */
-- 
1.9.3

  parent reply	other threads:[~2014-09-19 14:44 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-19 14:41 [Qemu-devel] [PULL 00/59] Block patches Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 01/59] block/vhdx.c: Mark parent_vhdx_guid variable as unused Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 02/59] ide/atapi: Mark non-data commands as complete Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 03/59] aio-win32: fix uninitialized use of have_select_revents Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 04/59] ide/ahci: Check for -ECANCELED in aio callbacks Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 05/59] block: Add refcnt in BlockDriverAIOCB Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 06/59] block: Add bdrv_aio_cancel_async Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 07/59] block: Drop bdrv_em_co_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 08/59] block: Drop bdrv_em_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 09/59] thread-pool: Convert thread_pool_aiocb_info.cancel to cancel_async Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 10/59] linux-aio: Convert laio_aiocb_info.cancel to .cancel_async Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 11/59] dma: Convert dma_aiocb_info.cancel " Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 12/59] iscsi: Convert iscsi_aiocb_info.cancel " Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 13/59] archipelago: Drop archipelago_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 14/59] blkdebug: Drop blkdebug_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 15/59] blkverify: Drop blkverify_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 16/59] curl: Drop curl_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 17/59] qed: Drop qed_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 18/59] quorum: fix quorum_aio_cancel() Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 19/59] quorum: Convert quorum_aiocb_info.cancel to .cancel_async Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 20/59] rbd: Drop rbd_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 21/59] sheepdog: Convert sd_aiocb_info.cancel to .cancel_async Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 22/59] win32-aio: Drop win32_aiocb_info.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 23/59] ide: Convert trim_aiocb_info.cancel to .cancel_async Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 24/59] block: Drop AIOCBInfo.cancel Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 25/59] block: Rename qemu_aio_release -> qemu_aio_unref Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 26/59] qdev-monitor: fix segmentation fault on qdev_device_help() Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 27/59] aio-win32: avoid out-of-bounds access to the events array Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 28/59] block: Introduce "null" drivers Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 29/59] qapi: Sort BlockdevDriver enum data list Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 30/59] qapi: Sort items in BlockdevOptions definition Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 31/59] qapi/block: Add "fatal" to BLOCK_IMAGE_CORRUPTED Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 32/59] qcow2: Add qcow2_signal_corruption() Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 33/59] qcow2: Use qcow2_signal_corruption() for overlaps Stefan Hajnoczi
2014-09-19 14:41 ` Stefan Hajnoczi [this message]
2014-09-19 14:41 ` [Qemu-devel] [PULL 35/59] iotests: Add more tests for qcow2 corruption Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 36/59] image-fuzzer: Trivial readability and formatting improvements Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 37/59] hmp: fix memory leak at hmp_info_block_jobs() Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 38/59] qcow2: Fix leak of QemuOpts in qcow2_open() Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 39/59] qapi: Allow enums in anonymous unions Stefan Hajnoczi
2014-09-19 14:41 ` [Qemu-devel] [PULL 40/59] qcow2: Add overlap-check.template option Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 41/59] qapi/block-core: Add "new" qcow2 options Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 42/59] docs: List all image elements currently supported by the fuzzer Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 43/59] fuzz: Add fuzzing functions for entries of refcount table and blocks Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 44/59] layout: Add generators for " Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 45/59] ahci: Adding basic functionality qtest Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 46/59] ahci: MSI capability should be at 0x80, not 0x50 Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 47/59] ahci: Add test_pci_spec to ahci-test Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 48/59] ahci: add test_pci_enable " Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 49/59] ahci: properly shadow the TFD register Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 50/59] ahci: Add test_hba_spec to ahci-test Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 51/59] ahci: Add test_hba_enable " Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 52/59] ahci: Add test_identify case " Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 53/59] block/archipelago: Fix typo in qemu_archipelago_truncate() Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 54/59] block: delete cow block driver Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 55/59] block: vhdx - fix reading beyond pointer during image creation Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 56/59] async: aio_context_new(): Handle event_notifier_init failure Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 57/59] virtio: Import virtio_vring.h Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 58/59] vring: Better error handling if num is too large Stefan Hajnoczi
2014-09-19 14:42 ` [Qemu-devel] [PULL 59/59] block: Always compile virtio-blk dataplane Stefan Hajnoczi
2014-09-19 18:10 ` [Qemu-devel] [PULL 00/59] Block patches Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1411137738-31280-35-git-send-email-stefanha@redhat.com \
    --to=stefanha@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).