[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 34/59] qcow2: Check L1/L2/reftable entries for alignm
From: |
Stefan Hajnoczi |
Subject: |
[Qemu-devel] [PULL 34/59] qcow2: Check L1/L2/reftable entries for alignment |
Date: |
Fri, 19 Sep 2014 15:41:53 +0100 |
From: Max Reitz <address@hidden>
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 <address@hidden>
Reviewed-by: Eric Blake <address@hidden>
Message-id: address@hidden
Signed-off-by: Stefan Hajnoczi <address@hidden>
---
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
- [Qemu-devel] [PULL 24/59] block: Drop AIOCBInfo.cancel, (continued)
- [Qemu-devel] [PULL 24/59] block: Drop AIOCBInfo.cancel, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 25/59] block: Rename qemu_aio_release -> qemu_aio_unref, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 27/59] aio-win32: avoid out-of-bounds access to the events array, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 28/59] block: Introduce "null" drivers, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 29/59] qapi: Sort BlockdevDriver enum data list, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 30/59] qapi: Sort items in BlockdevOptions definition, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 26/59] qdev-monitor: fix segmentation fault on qdev_device_help(), Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 32/59] qcow2: Add qcow2_signal_corruption(), Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 33/59] qcow2: Use qcow2_signal_corruption() for overlaps, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 31/59] qapi/block: Add "fatal" to BLOCK_IMAGE_CORRUPTED, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 34/59] qcow2: Check L1/L2/reftable entries for alignment,
Stefan Hajnoczi <=
- [Qemu-devel] [PULL 35/59] iotests: Add more tests for qcow2 corruption, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 36/59] image-fuzzer: Trivial readability and formatting improvements, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 38/59] qcow2: Fix leak of QemuOpts in qcow2_open(), Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 39/59] qapi: Allow enums in anonymous unions, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 37/59] hmp: fix memory leak at hmp_info_block_jobs(), Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 44/59] layout: Add generators for refcount table and blocks, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 43/59] fuzz: Add fuzzing functions for entries of refcount table and blocks, Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 46/59] ahci: MSI capability should be at 0x80, not 0x50., Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 45/59] ahci: Adding basic functionality qtest., Stefan Hajnoczi, 2014/09/19
- [Qemu-devel] [PULL 47/59] ahci: Add test_pci_spec to ahci-test., Stefan Hajnoczi, 2014/09/19