Skip to content

Commit

Permalink
qcow2: Validate refcount table offset
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
kevmw authored and stefanhaRH committed Apr 1, 2014
1 parent 5dab2fa commit 8c7de28
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
33 changes: 33 additions & 0 deletions block/qcow2.c
Expand Up @@ -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),
Expand Down Expand Up @@ -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;

Expand Down
13 changes: 13 additions & 0 deletions tests/qemu-iotests/080
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
10 changes: 10 additions & 0 deletions tests/qemu-iotests/080.out
Expand Up @@ -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

0 comments on commit 8c7de28

Please sign in to comment.