|
|
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 |
|