qcow2: Validate refcount table offset
The end of the refcount table must not exceed INT64_MAX so that integer overflows are avoided. Also check for misaligned refcount table. Such images are invalid and probably the result of data corruption. Error out to avoid further corruption. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
5dab2faddc
commit
8c7de28305
@ -329,6 +329,32 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t entries, size_t entry_len)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t size;
|
||||
|
||||
/* Use signed INT64_MAX as the maximum even for uint64_t header fields,
|
||||
* because values will be passed to qemu functions taking int64_t. */
|
||||
if (entries > INT64_MAX / entry_len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = entries * entry_len;
|
||||
|
||||
if (INT64_MAX - size < offset) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Tables must be cluster aligned */
|
||||
if (offset & (s->cluster_size - 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList qcow2_runtime_opts = {
|
||||
.name = "qcow2",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
|
||||
@ -590,6 +616,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = validate_table_offset(bs, s->refcount_table_offset,
|
||||
s->refcount_table_size, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Invalid reference count table offset");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->snapshots_offset = header.snapshots_offset;
|
||||
s->nb_snapshots = header.nb_snapshots;
|
||||
|
||||
|
@ -45,6 +45,7 @@ _supported_os Linux
|
||||
header_size=104
|
||||
|
||||
offset_backing_file_offset=8
|
||||
offset_refcount_table_offset=48
|
||||
offset_refcount_table_clusters=56
|
||||
offset_header_size=100
|
||||
offset_ext_magic=$header_size
|
||||
@ -76,6 +77,18 @@ poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\xff\xff\xff\xff"
|
||||
poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\x00\x02\x00\x01"
|
||||
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||
|
||||
echo
|
||||
echo "== Misaligned refcount table =="
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$offset_refcount_table_offset" "\x12\x34\x56\x78\x90\xab\xcd\xef"
|
||||
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||
|
||||
echo
|
||||
echo "== Huge refcount offset =="
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$offset_refcount_table_offset" "\xff\xff\xff\xff\xff\xff\x00\x00"
|
||||
poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\x00\x00\x00\x7f"
|
||||
{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
|
@ -20,4 +20,14 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large
|
||||
no file open, try 'help open'
|
||||
qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large
|
||||
no file open, try 'help open'
|
||||
|
||||
== Misaligned refcount table ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
|
||||
no file open, try 'help open'
|
||||
|
||||
== Huge refcount offset ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
|
||||
no file open, try 'help open'
|
||||
*** done
|
||||
|
Loading…
Reference in New Issue
Block a user