Misc qcow2 corruption checks
v2 cover Misc qcow2 corruption checks
This series contains a few checks that prevent QEMU from crashing under some scenarios with corrupted qcow2 images.
The first patch solves the crash reported here:
https://bugs.launchpad.net/qemu/+bug/1728615
And the others solve similar crashes that I detected in the process of fixing this one.
Regards,
Berto
v2:
- Use goto fail in the l2_allocate() check [Max]
- Add check and test case for allocation of compressed clusters [Max]
- Add test case for header.refcount_table_offset == 0
- Add overlap checks to qcow2_crypto_hdr_init_func() [Max]
v1: https://lists.gnu.org/archive/html/qemu-block/2017-11/msg00010.html
- Initial version
Output of backport-diff against v1:
Key:
[----] : patches are identical
[####] : number of functional differences between upstream/downstream patch [down] : patch is downstream-only
The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively
001/7:[----] [--] 'qcow2: Prevent allocating refcount blocks at offset 0' 002/7:[0003] [FC] 'qcow2: Prevent allocating L2 tables at offset 0' 003/7:[down] 'qcow2: Prevent allocating compressed clusters at offset 0' 004/7:[----] [-C] 'qcow2: Don't open images with header.refcount_table_clusters == 0' 005/7:[down] 'qcow2: Add iotest for an image with header.refcount_table_offset == 0' 006/7:[----] [--] 'qcow2: Add iotest for an empty refcount table' 007/7:[down] 'qcow2: Assert that the crypto header does not overlap other metadata'
Alberto Garcia (7):
qcow2: Prevent allocating refcount blocks at offset 0
qcow2: Prevent allocating L2 tables at offset 0
qcow2: Prevent allocating compressed clusters at offset 0
qcow2: Don't open images with header.refcount_table_clusters == 0
qcow2: Add iotest for an image with header.refcount_table_offset == 0
qcow2: Add iotest for an empty refcount table
qcow2: Assert that the crypto header does not overlap other metadata
block/qcow2-cluster.c | 8 ++++++++ block/qcow2-refcount.c | 15 +++++++++++++- block/qcow2.c | 7 +++++++ tests/qemu-iotests/060 | 49 ++++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/060.out | 39 ++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 1 deletion(-)
Signature · 1 line
2.11.0
PATCH v2 1/7 qcow2: Prevent allocating refcount blocks at offset 0
Each entry in the qcow2 cache contains an offset field indicating the location of the data in the qcow2 image. If the offset is 0 then it means that the entry contains no data and is available to be used when needed.
Because of that it is not possible to store in the cache the first cluster of the qcow2 image (offset = 0). This is not a problem because that cluster always contains the qcow2 header and we're not using this cache for that.
However, if the qcow2 image is corrupted it can happen that we try to allocate a new refcount block at offset 0, triggering this assertion and crashing QEMU:
qcow2_cache_entry_mark_dirty: Assertion `c->entries[i].offset != 0' failed
This patch adds an explicit check for this scenario and a new test case.
This problem was originally reported here:
https://bugs.launchpad.net/qemu/+bug/1728615
--- block/qcow2-refcount.c | 7 +++++++ tests/qemu-iotests/060 | 11 +++++++++++ tests/qemu-iotests/060.out | 8 ++++++++ 3 files changed, 26 insertions(+)
block/qcow2-refcount.c +7 -0
@@ -367,6 +367,13 @@ static int alloc_refcount_block(BlockDriverState *bs,return new_block; } +/* If we're allocating the block at offset 0 then something is wrong */ +if (new_block ==0 ) { +qcow2_signal_corruption (bs, true,-1 ,-1 ,"Preventing invalid " +"allocation of refcount block at offset 0" ); +return -EIO ; + } +#ifdef DEBUG_ALLOC2 fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64 " at %" PRIx64 "\n",
tests/qemu-iotests/060 +11 -0
@@ -242,6 +242,17 @@ poke_file "$TEST_IMG" "$(($l2_offset+8))" "\x80\x00\x00\x00\x00\x06\x2a\x00" # Should emit two error messages $QEMU_IO -c "discard 0 64k" -c "read 64k 64k" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing empty refcount table with valid L1 and L2 tables ===" +echo +_make_test_img 64M +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +# Since the first data cluster is already allocated this triggers an +# allocation with an explicit offset (using qcow2_alloc_clusters_at()) +# causing a refcount block to be allocated at offset 0 +$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full
tests/qemu-iotests/060.out +8 -0
@@ -181,4 +181,12 @@ qcow2: Marking image as corrupt: Cluster allocation offset 0x62a00 unaligned (L2 discard 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read failed: Input/output error + +=== Testing empty refcount table with valid L1 and L2 tables === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2: Marking image as corrupt: Preventing invalid allocation of refcount block at offset 0; further corruption events will be suppressed +write failed: Input/output error *** done
Signature · 1 line
2.11.0
PATCH v2 2/7 qcow2: Prevent allocating L2 tables at offset 0
In reply to Alberto GarciaIf the refcount data is corrupted then we can end up trying to allocate a new L2 table at offset 0 in the image, triggering an assertion in the qcow2 cache that would crash QEMU:
qcow2_cache_entry_mark_dirty: Assertion `c->entries[i].offset != 0' failed
This patch adds an explicit check for this scenario and a new test case.
--- block/qcow2-cluster.c | 8 ++++++++ tests/qemu-iotests/060 | 7 +++++++ tests/qemu-iotests/060.out | 6 ++++++ 3 files changed, 21 insertions(+)
block/qcow2-cluster.c +8 -0
@@ -278,6 +278,14 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) goto fail; } +/* If we're allocating the table at offset 0 then something is wrong */ +if (l2_offset ==0 ) { +qcow2_signal_corruption (bs, true,-1 ,-1 ,"Preventing invalid " +"allocation of L2 table at offset 0" ); + ret = -EIO ; + goto fail; + } + ret =qcow2_cache_flush (bs, s->refcount_block_cache);if (ret <0 ) { goto fail;
tests/qemu-iotests/060 +7 -0
@@ -253,6 +253,13 @@ poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" # causing a refcount block to be allocated at offset 0 $QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing empty refcount block ===" +echo +_make_test_img 64M +poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full
tests/qemu-iotests/060.out +6 -0
@@ -189,4 +189,10 @@ wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Preventing invalid allocation of refcount block at offset 0; further corruption events will be suppressed write failed: Input/output error + +=== Testing empty refcount block === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed +write failed: Input/output error *** done
Signature · 1 line
2.11.0
PATCH v2 3/7 qcow2: Prevent allocating compressed clusters at offset 0
In reply to Alberto GarciaIf the refcount data is corrupted then we can end up trying to allocate a new compressed cluster at offset 0 in the image, triggering an assertion in qcow2_alloc_bytes() that would crash QEMU:
qcow2_alloc_bytes: Assertion `offset' failed.
This patch adds an explicit check for this scenario and a new test case.
--- block/qcow2-refcount.c | 8 +++++++- tests/qemu-iotests/060 | 10 ++++++++++ tests/qemu-iotests/060.out | 8 ++++++++ 3 files changed, 25 insertions(+), 1 deletion(-)
block/qcow2-refcount.c +7 -1
@@ -1082,6 +1082,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)return new_cluster; } +if (new_cluster ==0 ) { +qcow2_signal_corruption (bs, true,-1 ,-1 ,"Preventing invalid " +"allocation of compressed cluster " +"at offset 0" ); +return -EIO ; + } +if (!offset ||ROUND_UP (offset, s->cluster_size) != new_cluster) { offset = new_cluster; free_in_cluster = s->cluster_size;
@@ -1090,7 +1097,6 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } } -assert (offset); ret =update_refcount (bs, offset, size,1 , false,QCOW2_DISCARD_NEVER );if (ret <0 ) { offset =0 ;
tests/qemu-iotests/060 +10 -0
@@ -260,6 +260,16 @@ _make_test_img 64M poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" $QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing empty refcount block with compressed write ===" +echo +_make_test_img 64M +$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +# The previous write already allocated an L2 table, so now this new +# write will try to allocate a compressed data cluster at offset 0. +$QEMU_IO -c "write -c 0k 64k" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full
tests/qemu-iotests/060.out +8 -0
@@ -195,4 +195,12 @@ write failed: Input/output error Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed write failed: Input/output error + +=== Testing empty refcount block with compressed write === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2: Marking image as corrupt: Preventing invalid allocation of compressed cluster at offset 0; further corruption events will be suppressed +write failed: Input/output error *** done
Signature · 1 line
2.11.0
PATCH v2 5/7 qcow2: Add iotest for an image with header.refcount_table_offset == 0
In reply to Alberto GarciaThis patch adds a simple iotest in which we try to write to an image with the refcount table offset set to 0.
This scenario was already handled by the existing consistency checks, but we add an explicit test case for completeness.
--- tests/qemu-iotests/060 | 7 +++++++ tests/qemu-iotests/060.out | 6 ++++++ 2 files changed, 13 insertions(+)
tests/qemu-iotests/060 +7 -0
@@ -277,6 +277,13 @@ _make_test_img 64M poke_file "$TEST_IMG" "56" "\x00\x00\x00\x00" $QEMU_IO -c "write 0 64k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt +echo +echo "=== Testing incorrect refcount table offset ===" +echo +_make_test_img 64M +poke_file "$TEST_IMG" "48" "\x00\x00\x00\x00\x00\x00\x00\x00" +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full
tests/qemu-iotests/060.out +6 -0
@@ -208,4 +208,10 @@ write failed: Input/output error Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 can't open device TEST_DIR/t.IMGFMT: Image does not contain a reference count table + +=== Testing incorrect refcount table offset === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed +write failed: Input/output error *** done
Signature · 1 line
2.11.0
PATCH v2 6/7 qcow2: Add iotest for an empty refcount table
In reply to Alberto GarciaThis patch adds a simple iotest in which we try to write to an image with an empty refcount table (i.e. with all entries set to 0).
This scenario was already handled by the existing consistency checks, but we add an explicit test case for completeness.
--- tests/qemu-iotests/060 | 7 +++++++ tests/qemu-iotests/060.out | 6 ++++++ 2 files changed, 13 insertions(+)
tests/qemu-iotests/060 +7 -0
@@ -243,6 +243,13 @@ poke_file "$TEST_IMG" "$(($l2_offset+8))" "\x80\x00\x00\x00\x00\x06\x2a\x00" $QEMU_IO -c "discard 0 64k" -c "read 64k 64k" "$TEST_IMG" | _filter_qemu_io echo +echo "=== Testing empty refcount table ===" +echo +_make_test_img 64M +poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io + +echo echo "=== Testing empty refcount table with valid L1 and L2 tables ===" echo _make_test_img 64M
tests/qemu-iotests/060.out +6 -0
@@ -182,6 +182,12 @@ discard 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read failed: Input/output error +=== Testing empty refcount table === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with refcount table); further corruption events will be suppressed +write failed: Input/output error + === Testing empty refcount table with valid L1 and L2 tables === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Signature · 1 line
2.11.0
PATCH v2 7/7 qcow2: Assert that the crypto header does not overlap other metadata
In reply to Alberto GarciaThe crypto header is initialized only when QEMU is creating a new image, so there's no chance of this happening on a corrupted image.
If QEMU is really trying to allocate the header overlapping other existing metadata sections then this is a serious bug in QEMU itself so let's add an assertion.
--- block/qcow2.c | 1 + 1 file changed, 1 insertion(+)
block/qcow2.c +1 -0
@@ -126,6 +126,7 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,/* Zero fill remaining space in cluster so it has predictable * content in case of future spec changes */ clusterlen =size_to_clusters (s, headerlen) * s->cluster_size; +assert (qcow2_pre_write_overlap_check (bs,0 , ret, clusterlen) ==0 ); ret =bdrv_pwrite_zeroes (bs->file, ret + headerlen, clusterlen - headerlen,0 );
Signature · 1 line
2.11.0
PATCH v2 4/7 qcow2: Don't open images with header.refcount_table_clusters == 0
In reply to Alberto Garciaqcow2_do_open() is checking that header.refcount_table_clusters is not too large, but it doesn't check that it's greater than zero. Apart from the fact that an image like that is obviously corrupted, trying to use it crashes QEMU since we end up with a null s->refcount_table after qcow2_refcount_init().
These images can however be repaired, so allow opening them if the BDRV_O_CHECK flag is set.
--- block/qcow2.c | 6 ++++++ tests/qemu-iotests/060 | 7 +++++++ tests/qemu-iotests/060.out | 5 +++++ 3 files changed, 18 insertions(+)
block/qcow2.c +6 -0
@@ -1280,6 +1280,12 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } +if (header.refcount_table_clusters ==0 && !(flags &BDRV_O_CHECK )) { +error_setg (errp,"Image does not contain a reference count table" ); + ret = -EINVAL ; + goto fail; + } + ret =validate_table_offset (bs, s->refcount_table_offset, s->refcount_table_size,sizeof (uint64_t ));if (ret <0 ) {
tests/qemu-iotests/060 +7 -0
@@ -270,6 +270,13 @@ poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" # write will try to allocate a compressed data cluster at offset 0. $QEMU_IO -c "write -c 0k 64k" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing zero refcount table size ===" +echo +_make_test_img 64M +poke_file "$TEST_IMG" "56" "\x00\x00\x00\x00" +$QEMU_IO -c "write 0 64k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + # success, all done echo "*** done" rm -f $seq.full
tests/qemu-iotests/060.out +5 -0
@@ -203,4 +203,9 @@ wrote 65536/65536 bytes at offset 65536 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Preventing invalid allocation of compressed cluster at offset 0; further corruption events will be suppressed write failed: Input/output error + +=== Testing zero refcount table size === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +can't open device TEST_DIR/t.IMGFMT: Image does not contain a reference count table *** done
Signature · 1 line
2.11.0
Re: [Qemu-devel] [PATCH v2 7/7] qcow2: Assert that the crypto header does not overlap other metadata
In reply to Alberto GarciaQuoted 11 lines · Alberto Garcia
The crypto header is initialized only when QEMU is creating a new image, so there's no chance of this happening on a corrupted image.
If QEMU is really trying to allocate the header overlapping other existing metadata sections then this is a serious bug in QEMU itself so let's add an assertion.
Signed-off-by Alberto Garcia <[email protected]>--- block/qcow2.c | 1 + 1 file changed, 1 insertion(+)
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH v2 3/7] qcow2: Prevent allocating compressed clusters at offset 0
In reply to Alberto GarciaQuoted 35 lines · Alberto Garcia
If the refcount data is corrupted then we can end up trying to allocate a new compressed cluster at offset 0 in the image, triggering an assertion in
qcow2_alloc_bytes()that would crash QEMU:
qcow2_alloc_bytes: Assertion `offset' failed.This patch adds an explicit check for this scenario and a new test case.
Signed-off-by Alberto Garcia <[email protected]>--- block/qcow2-refcount.c | 8 +++++++- tests/qemu-iotests/060 | 10 ++++++++++ tests/qemu-iotests/060.out | 8 ++++++++ 3 files changed, 25 insertions(+), 1 deletion(-)block/qcow2-refcount.c +7
-0@@ -1082,6 +1082,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)return new_cluster; } +if (new_cluster ==0 ) { +qcow2_signal_corruption (bs, true,-1 ,-1 ,"Preventing invalid " +"allocation of compressed cluster " +"at offset 0" ); +return -EIO ; + } +if (!offset ||ROUND_UP (offset, s->cluster_size) != new_cluster) { offset = new_cluster; free_in_cluster = s->cluster_size;
block/qcow2-refcount.c int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) · line 1093
Quoted 4 lines
block/qcow2-refcount.c +0
-1@@ -1090,7 +1097,6 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } } -assert (offset);
I don't think this assert() was meant as a protection against offset being 0. :-)
if the assert() stays.
Quoted 42 lines
block/qcow2-refcount.c +0
-0ret =update_refcount (bs, offset, size,1 , false,QCOW2_DISCARD_NEVER );if (ret <0 ) { offset =0 ;tests/qemu-iotests/060 +10
-0@@ -260,6 +260,16 @@ _make_test_img 64M poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" $QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing empty refcount block with compressed write ===" +echo +_make_test_img 64M +$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +# The previous write already allocated an L2 table, so now this new +# write will try to allocate a compressed data cluster at offset 0. +$QEMU_IO -c "write -c 0k 64k" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.fulltests/qemu-iotests/060.out +8
-0@@ -195,4 +195,12 @@ write failed: Input/output error Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed write failed: Input/output error + +=== Testing empty refcount block with compressed write === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2: Marking image as corrupt: Preventing invalid allocation of compressed cluster at offset 0; further corruption events will be suppressed +write failed: Input/output error *** done
Re: [Qemu-devel] [PATCH v2 5/7] qcow2: Add iotest for an image with header.refcount_table_offset == 0
In reply to Alberto GarciaQuoted 11 lines · Alberto Garcia
This patch adds a simple iotest in which we try to write to an image with the refcount table offset set to 0.
This scenario was already handled by the existing consistency checks, but we add an explicit test case for completeness.
Signed-off-by Alberto Garcia <[email protected]>--- tests/qemu-iotests/060 | 7 +++++++ tests/qemu-iotests/060.out | 6 ++++++ 2 files changed, 13 insertions(+)
Max Reitz
AppliedQuoted 48 lines · Alberto Garcia
This series contains a few checks that prevent QEMU from crashing under some scenarios with corrupted qcow2 images.
The first patch solves the crash reported here:
https://bugs.launchpad.net/qemu/+bug/1728615
And the others solve similar crashes that I detected in the process of fixing this one.
Regards,
Berto
v2:
- Use goto fail in thel2_allocate()check [Max]
- Add check and test case for allocation of compressed clusters [Max]
- Add test case for header.refcount_table_offset == 0
- Add overlap checks toqcow2_crypto_hdr_init_func()[Max]v1: https://lists.gnu.org/archive/html/qemu-block/2017-11/msg00010.html
- Initial versionOutput of backport-diff against v1:
Key:
[----] : patches are identical
[####] : number of functional differences between upstream/downstream patch [down] : patch is downstream-only
The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively001/7:[----] [--] 'qcow2: Prevent allocating refcount blocks at offset 0' 002/7:[0003] [FC] 'qcow2: Prevent allocating L2 tables at offset 0' 003/7:[down] 'qcow2: Prevent allocating compressed clusters at offset 0' 004/7:[----] [-C] 'qcow2: Don't open images with header.refcount_table_clusters == 0' 005/7:[down] 'qcow2: Add iotest for an image with header.refcount_table_offset == 0' 006/7:[----] [--] 'qcow2: Add iotest for an empty refcount table' 007/7:[down] 'qcow2: Assert that the crypto header does not overlap other metadata'
Alberto Garcia (7):
qcow2: Prevent allocating refcount blocks at offset 0
qcow2: Prevent allocating L2 tables at offset 0
qcow2: Prevent allocating compressed clusters at offset 0
qcow2: Don't open images with header.refcount_table_clusters == 0
qcow2: Add iotest for an image with header.refcount_table_offset == 0
qcow2: Add iotest for an empty refcount table
qcow2: Assert that the crypto header does not overlap other metadata
Quoted 6 lines
block/qcow2-cluster.c | 8 ++++++++ block/qcow2-refcount.c | 15 +++++++++++++- block/qcow2.c | 7 +++++++ tests/qemu-iotests/060 | 49 ++++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/060.out | 39 ++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 1 deletion(-)
Thanks, applied to my block branch:
https://github.com/XanClic/qemu/commits/block
Max
Re: [Qemu-devel] [PATCH v2 3/7] qcow2: Prevent allocating compressed clusters at offset 0
In reply to Max ReitzQuoted 14 lines · Max Reitz
Quoted 14 lines
block/qcow2-refcount.c +7
-0+if (new_cluster ==0 ) { +qcow2_signal_corruption (bs, true,-1 ,-1 ,"Preventing invalid " +"allocation of compressed cluster " +"at offset 0" ); +return -EIO ; + } +if (!offset ||ROUND_UP (offset, s->cluster_size) != new_cluster) { offset = new_cluster; free_in_cluster = s->cluster_size;@@ -1090,7 +1097,6 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } }
Quoted 4 lines
block/qcow2-refcount.c int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) · line 1093
Quoted 1 line
block/qcow2-refcount.c +0
-1-assert (offset);I don't think this
assert()was meant as a protection against offset being 0. :-)
After the new check offset is now guaranteed to be 0, so what's the point of keeping the assert() ?
Berto
Re: [Qemu-devel] [Qemu-block] [PATCH v2 3/7] qcow2: Prevent allocating compressed clusters at offset 0
Quoted 7 lines · Alberto Garcia
Quoted 4 lines
Quoted 1 line
- assert(offset);
I don't think this
assert()was meant as a protection against offset being 0. :-)After the new check offset is now guaranteed to be 0, so what's the point of keeping the
assert()?
I meant "guaranteed _not_ to be 0" :-)
Berto
Re: [Qemu-devel] [Qemu-block] [PATCH v2 3/7] qcow2: Prevent allocating compressed clusters at offset 0
Quoted 10 lines · Alberto Garcia
Quoted 7 lines · Alberto Garcia
Quoted 4 lines
Quoted 1 line
- assert(offset);
I don't think this
assert()was meant as a protection against offset being 0. :-)After the new check offset is now guaranteed to be 0, so what's the point of keeping the
assert()?I meant "guaranteed _not_ to be 0" :-)
That is the point of an assert.
An assert should not guard against something that can occur. It should express that something will always be true (in this case that the offset is guaranteed not to be 0). Then, someone who reads the code does not have to read all code paths to check whether that condition is true.
If an assert checks a condition that can be true, it's wrong. Then either the code is buggy (like it was before this patch) or the error should be handled gracefully instead of aborting the program.
In a perfect world, all assert()s would be checked at compile time.
Max
Re: [Qemu-devel] [Qemu-block] [PATCH v2 3/7] qcow2: Prevent allocating compressed clusters at offset 0
Quoted 8 lines · Max Reitz
Quoted 8 lines
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
- assert(offset);
I don't think this
assert()was meant as a protection against offset being 0. :-)After the new check offset is now guaranteed to be 0, so what's the point of keeping the
assert()?
Quoted 7 lines
Quoted 1 line
I meant "guaranteed _not_ to be 0" :-)
That is the point of an assert.
An assert should not guard against something that can occur. It should express that something will always be true (in this case that the offset is guaranteed not to be 0).
Right, and they're especially useful when it's not obvious that the assertion is always true.
The reason why I removed the assertion in this case is that the code that checks and guarantees that the offset is not null is immediately before the assert() line.
But I'm not going to argue over something like this, I don't mind if you prefer to keep it :-)
Berto
Re: [Qemu-devel] [Qemu-block] [PATCH v2 4/7] qcow2: Don't open images with header.refcount_table_clusters == 0
In reply to Alberto GarciaAm 03.11.2017 um 15:18 hat Alberto Garcia geschrieben:
Quoted 15 lines
qcow2_do_open()is checking that header.refcount_table_clusters is not too large, but it doesn't check that it's greater than zero. Apart from the fact that an image like that is obviously corrupted, trying to use it crashes QEMU since we end up with a nulls->refcount_tableafterqcow2_refcount_init().These images can however be repaired, so allow opening them if the
BDRV_O_CHECKflag is set.Signed-off-by Alberto Garcia <[email protected]>Reviewed-by Max Reitz <[email protected]>
tests/qemu-iotests/060 poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" · line 278
Quoted 9 lines
tests/qemu-iotests/060 +6
-0@@ -270,6 +270,13 @@ poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" # write will try to allocate a compressed data cluster at offset 0. $QEMU_IO -c "write -c 0k 64k" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing zero refcount table size ===" +echo +_make_test_img 64M +poke_file "$TEST_IMG" "56" "\x00\x00\x00\x00" +$QEMU_IO -c "write 0 64k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
In the commit message, you claim that the image can be repaired. Would it be worth actually testing the repair here?
Kevin
Re: [Qemu-devel] [Qemu-block] [PATCH v2 4/7] qcow2: Don't open images with header.refcount_table_clusters == 0
Quoted 9 lines · Kevin Wolf
tests/qemu-iotests/060 poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" · line 278
Quoted 6 lines
tests/qemu-iotests/060 +6
-0+echo +echo "=== Testing zero refcount table size ===" +echo +_make_test_img 64M +poke_file "$TEST_IMG" "56" "\x00\x00\x00\x00" +$QEMU_IO -c "write 0 64k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmtIn the commit message, you claim that the image can be repaired. Would it be worth actually testing the repair here?
Good idea. Max already merged this series in his branch, so I'll just send a follow-up patch with this.
Berto