0a122b
From 6707cb9501355de8244d224b3a8140c0fe62a983 Mon Sep 17 00:00:00 2001
0a122b
From: Kevin Wolf <kwolf@redhat.com>
0a122b
Date: Tue, 25 Mar 2014 14:23:30 +0100
0a122b
Subject: [PATCH 23/49] qcow2: Validate refcount table offset
0a122b
0a122b
RH-Author: Kevin Wolf <kwolf@redhat.com>
0a122b
Message-id: <1395753835-7591-24-git-send-email-kwolf@redhat.com>
0a122b
Patchwork-id: n/a
0a122b
O-Subject: [virt-devel] [EMBARGOED RHEL-7.0 qemu-kvm PATCH 23/48] qcow2: Validate refcount table offset
0a122b
Bugzilla: 1066691
0a122b
RH-Acked-by: Jeff Cody <jcody@redhat.com>
0a122b
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
0a122b
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
0a122b
0a122b
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1066691
0a122b
Upstream status: Series embargoed
0a122b
0a122b
The end of the refcount table must not exceed INT64_MAX so that integer
0a122b
overflows are avoided.
0a122b
0a122b
Also check for misaligned refcount table. Such images are invalid and
0a122b
probably the result of data corruption. Error out to avoid further
0a122b
corruption.
0a122b
0a122b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
0a122b
---
0a122b
 block/qcow2.c              |   33 +++++++++++++++++++++++++++++++++
0a122b
 tests/qemu-iotests/080     |   13 +++++++++++++
0a122b
 tests/qemu-iotests/080.out |   10 ++++++++++
0a122b
 3 files changed, 56 insertions(+), 0 deletions(-)
0a122b
0a122b
diff --git a/block/qcow2.c b/block/qcow2.c
0a122b
index a7780ac..5513fac 100644
0a122b
--- a/block/qcow2.c
0a122b
+++ b/block/qcow2.c
0a122b
@@ -329,6 +329,32 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
0a122b
     return ret;
0a122b
 }
0a122b
 
0a122b
+static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
0a122b
+                                 uint64_t entries, size_t entry_len)
0a122b
+{
0a122b
+    BDRVQcowState *s = bs->opaque;
0a122b
+    uint64_t size;
0a122b
+
0a122b
+    /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
0a122b
+     * because values will be passed to qemu functions taking int64_t. */
0a122b
+    if (entries > INT64_MAX / entry_len) {
0a122b
+        return -EINVAL;
0a122b
+    }
0a122b
+
0a122b
+    size = entries * entry_len;
0a122b
+
0a122b
+    if (INT64_MAX - size < offset) {
0a122b
+        return -EINVAL;
0a122b
+    }
0a122b
+
0a122b
+    /* Tables must be cluster aligned */
0a122b
+    if (offset & (s->cluster_size - 1)) {
0a122b
+        return -EINVAL;
0a122b
+    }
0a122b
+
0a122b
+    return 0;
0a122b
+}
0a122b
+
0a122b
 static QemuOptsList qcow2_runtime_opts = {
0a122b
     .name = "qcow2",
0a122b
     .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
0a122b
@@ -589,6 +615,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
0a122b
         goto fail;
0a122b
     }
0a122b
 
0a122b
+    ret = validate_table_offset(bs, s->refcount_table_offset,
0a122b
+                                s->refcount_table_size, sizeof(uint64_t));
0a122b
+    if (ret < 0) {
0a122b
+        error_setg(errp, "Invalid reference count table offset");
0a122b
+        goto fail;
0a122b
+    }
0a122b
+
0a122b
     s->snapshots_offset = header.snapshots_offset;
0a122b
     s->nb_snapshots = header.nb_snapshots;
0a122b
 
0a122b
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
0a122b
index 6179e05..f58ac73 100755
0a122b
--- a/tests/qemu-iotests/080
0a122b
+++ b/tests/qemu-iotests/080
0a122b
@@ -45,6 +45,7 @@ _supported_os Linux
0a122b
 header_size=104
0a122b
 
0a122b
 offset_backing_file_offset=8
0a122b
+offset_refcount_table_offset=48
0a122b
 offset_refcount_table_clusters=56
0a122b
 offset_header_size=100
0a122b
 offset_ext_magic=$header_size
0a122b
@@ -76,6 +77,18 @@ poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\xff\xff\xff\xff"
0a122b
 poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\x00\x02\x00\x01"
0a122b
 { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
0a122b
 
0a122b
+echo
0a122b
+echo "== Misaligned refcount table =="
0a122b
+_make_test_img 64M
0a122b
+poke_file "$TEST_IMG" "$offset_refcount_table_offset" "\x12\x34\x56\x78\x90\xab\xcd\xef"
0a122b
+{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
0a122b
+
0a122b
+echo
0a122b
+echo "== Huge refcount offset =="
0a122b
+_make_test_img 64M
0a122b
+poke_file "$TEST_IMG" "$offset_refcount_table_offset" "\xff\xff\xff\xff\xff\xff\x00\x00"
0a122b
+poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\x00\x00\x00\x7f"
0a122b
+{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
0a122b
 
0a122b
 # success, all done
0a122b
 echo "*** done"
0a122b
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
0a122b
index 6fef6d9..f919b58 100644
0a122b
--- a/tests/qemu-iotests/080.out
0a122b
+++ b/tests/qemu-iotests/080.out
0a122b
@@ -20,4 +20,14 @@ qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large
0a122b
 no file open, try 'help open'
0a122b
 qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large
0a122b
 no file open, try 'help open'
0a122b
+
0a122b
+== Misaligned refcount table ==
0a122b
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
0a122b
+qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
0a122b
+no file open, try 'help open'
0a122b
+
0a122b
+== Huge refcount offset ==
0a122b
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
0a122b
+qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
0a122b
+no file open, try 'help open'
0a122b
 *** done
0a122b
-- 
0a122b
1.7.1
0a122b