qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows
@ 2013-05-13 13:42 Kevin Wolf
  2013-05-13 13:42 ` [Qemu-devel] [PATCH for-1.5 1/3] " Kevin Wolf
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Kevin Wolf @ 2013-05-13 13:42 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, rjones, stefanha

Suggestions for a better test case are welcome. But now that creating a large
image file fails, and if you somehow manage to create it anyway (qcow2.py)
opening it fails, it's hard to test the actual read/write cases.

Kevin Wolf (3):
  qcow2: Catch some L1 table index overflows
  qcow2.py: Subcommand for changing header fields
  qemu-iotests: Try creating huge qcow2 image

 block/qcow2-cluster.c        | 23 ++++++++++++------
 block/qcow2.c                | 13 ++++++++--
 block/qcow2.h                |  5 ++--
 tests/qemu-iotests/054       | 58 ++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/054.out   | 10 ++++++++
 tests/qemu-iotests/common.rc |  2 +-
 tests/qemu-iotests/group     |  1 +
 tests/qemu-iotests/qcow2.py  | 12 +++++++++
 8 files changed, 111 insertions(+), 13 deletions(-)
 create mode 100755 tests/qemu-iotests/054
 create mode 100644 tests/qemu-iotests/054.out

-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH for-1.5 1/3] qcow2: Catch some L1 table index overflows
  2013-05-13 13:42 [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Kevin Wolf
@ 2013-05-13 13:42 ` Kevin Wolf
  2013-05-13 13:42 ` [Qemu-devel] [PATCH 2/3] qcow2.py: Subcommand for changing header fields Kevin Wolf
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Kevin Wolf @ 2013-05-13 13:42 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, rjones, stefanha

This catches the situation that is described in the bug report at
https://bugs.launchpad.net/qemu/+bug/865518 and goes like this:

    $ qemu-img create -f qcow2 huge.qcow2 $((1024*1024))T
    Formatting 'huge.qcow2', fmt=qcow2 size=1152921504606846976 encryption=off cluster_size=65536 lazy_refcounts=off
    $ qemu-io /tmp/huge.qcow2 -c "write $((1024*1024*1024*1024*1024*1024 - 1024)) 512"
    Segmentation fault

With this patch applied the segfault will be avoided, however the case
will still fail, though gracefully:

    $ qemu-img create -f qcow2 /tmp/huge.qcow2 $((1024*1024))T
    Formatting 'huge.qcow2', fmt=qcow2 size=1152921504606846976 encryption=off cluster_size=65536 lazy_refcounts=off
    qemu-img: The image size is too large for file format 'qcow2'

Note that even long before these overflow checks kick in, you get
insanely high memory usage (up to INT_MAX * sizeof(uint64_t) = 16 GB for
the L1 table), so with somewhat smaller image sizes you'll probably see
qemu aborting for a failed g_malloc().

If you need huge image sizes, you should increase the cluster size to
the maximum of 2 MB in order to get higher limits.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/qcow2-cluster.c | 23 +++++++++++++++--------
 block/qcow2.c         | 13 +++++++++++--
 block/qcow2.h         |  5 +++--
 3 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index c71470a..76f30e5 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -29,12 +29,13 @@
 #include "block/qcow2.h"
 #include "trace.h"
 
-int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
+int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
+                        bool exact_size)
 {
     BDRVQcowState *s = bs->opaque;
-    int new_l1_size, new_l1_size2, ret, i;
+    int new_l1_size2, ret, i;
     uint64_t *new_l1_table;
-    int64_t new_l1_table_offset;
+    int64_t new_l1_table_offset, new_l1_size;
     uint8_t data[12];
 
     if (min_size <= s->l1_size)
@@ -53,8 +54,13 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
         }
     }
 
+    if (new_l1_size > INT_MAX) {
+        return -EFBIG;
+    }
+
 #ifdef DEBUG_ALLOC2
-    fprintf(stderr, "grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
+    fprintf(stderr, "grow l1_table from %d to %" PRId64 "\n",
+            s->l1_size, new_l1_size);
 #endif
 
     new_l1_size2 = sizeof(uint64_t) * new_l1_size;
@@ -391,8 +397,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
     int *num, uint64_t *cluster_offset)
 {
     BDRVQcowState *s = bs->opaque;
-    unsigned int l1_index, l2_index;
-    uint64_t l2_offset, *l2_table;
+    unsigned int l2_index;
+    uint64_t l1_index, l2_offset, *l2_table;
     int l1_bits, c;
     unsigned int index_in_cluster, nb_clusters;
     uint64_t nb_available, nb_needed;
@@ -507,8 +513,8 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
                              int *new_l2_index)
 {
     BDRVQcowState *s = bs->opaque;
-    unsigned int l1_index, l2_index;
-    uint64_t l2_offset;
+    unsigned int l2_index;
+    uint64_t l1_index, l2_offset;
     uint64_t *l2_table = NULL;
     int ret;
 
@@ -522,6 +528,7 @@ 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;
 
     /* seek the l2 table of the given l2 offset */
diff --git a/block/qcow2.c b/block/qcow2.c
index 2e346d8..0fa5cb2 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -307,6 +307,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     QemuOpts *opts;
     Error *local_err = NULL;
     uint64_t ext_end;
+    uint64_t l1_vm_state_index;
 
     ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
     if (ret < 0) {
@@ -424,7 +425,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
     /* read the level 1 table */
     s->l1_size = header.l1_size;
-    s->l1_vm_state_index = size_to_l1(s, header.size);
+
+    l1_vm_state_index = size_to_l1(s, header.size);
+    if (l1_vm_state_index > INT_MAX) {
+        ret = -EFBIG;
+        goto fail;
+    }
+    s->l1_vm_state_index = l1_vm_state_index;
+
     /* the L1 table must contain at least enough entries to put
        header.size bytes */
     if (s->l1_size < s->l1_vm_state_index) {
@@ -1480,7 +1488,8 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
 static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
 {
     BDRVQcowState *s = bs->opaque;
-    int ret, new_l1_size;
+    int64_t new_l1_size;
+    int ret;
 
     if (offset & 511) {
         error_report("The new size must be a multiple of 512");
diff --git a/block/qcow2.h b/block/qcow2.h
index 9421843..6959c6a 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -284,7 +284,7 @@ static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
     return (size + (s->cluster_size - 1)) >> s->cluster_bits;
 }
 
-static inline int size_to_l1(BDRVQcowState *s, int64_t size)
+static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size)
 {
     int shift = s->cluster_bits + s->l2_bits;
     return (size + (1ULL << shift) - 1) >> shift;
@@ -360,7 +360,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                           BdrvCheckMode fix);
 
 /* qcow2-cluster.c functions */
-int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);
+int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
+                        bool exact_size);
 void qcow2_l2_cache_reset(BlockDriverState *bs);
 int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
 void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH 2/3] qcow2.py: Subcommand for changing header fields
  2013-05-13 13:42 [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Kevin Wolf
  2013-05-13 13:42 ` [Qemu-devel] [PATCH for-1.5 1/3] " Kevin Wolf
@ 2013-05-13 13:42 ` Kevin Wolf
  2013-05-13 14:59   ` Stefan Hajnoczi
  2013-05-13 13:42 ` [Qemu-devel] [PATCH 3/3] qemu-iotests: Try creating huge qcow2 image Kevin Wolf
  2013-05-13 14:39 ` [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Richard W.M. Jones
  3 siblings, 1 reply; 6+ messages in thread
From: Kevin Wolf @ 2013-05-13 13:42 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, rjones, stefanha

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/qemu-iotests/qcow2.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py
index fecf5b9..b6abd16 100755
--- a/tests/qemu-iotests/qcow2.py
+++ b/tests/qemu-iotests/qcow2.py
@@ -149,6 +149,17 @@ def cmd_dump_header(fd):
     h.dump()
     h.dump_extensions()
 
+def cmd_set_header(fd, name, value):
+    try:
+        value = int(value, 0)
+    except:
+        print "'%s' is not a valid number" % value
+        sys.exit(1)
+
+    h = QcowHeader(fd)
+    h.__dict__[name] = value
+    h.update(fd)
+
 def cmd_add_header_ext(fd, magic, data):
     try:
         magic = int(magic, 0)
@@ -205,6 +216,7 @@ def cmd_set_feature_bit(fd, group, bit):
 
 cmds = [
     [ 'dump-header',    cmd_dump_header,    0, 'Dump image header and header extensions' ],
+    [ 'set-header',     cmd_set_header,     2, 'Set a field in the header'],
     [ 'add-header-ext', cmd_add_header_ext, 2, 'Add a header extension' ],
     [ 'del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension' ],
     [ 'set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'],
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH 3/3] qemu-iotests: Try creating huge qcow2 image
  2013-05-13 13:42 [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Kevin Wolf
  2013-05-13 13:42 ` [Qemu-devel] [PATCH for-1.5 1/3] " Kevin Wolf
  2013-05-13 13:42 ` [Qemu-devel] [PATCH 2/3] qcow2.py: Subcommand for changing header fields Kevin Wolf
@ 2013-05-13 13:42 ` Kevin Wolf
  2013-05-13 14:39 ` [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Richard W.M. Jones
  3 siblings, 0 replies; 6+ messages in thread
From: Kevin Wolf @ 2013-05-13 13:42 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, rjones, stefanha

It's supposed to fail gracefully instead of segfaulting.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/qemu-iotests/054       | 58 ++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/054.out   | 10 ++++++++
 tests/qemu-iotests/common.rc |  2 +-
 tests/qemu-iotests/group     |  1 +
 4 files changed, 70 insertions(+), 1 deletion(-)
 create mode 100755 tests/qemu-iotests/054
 create mode 100644 tests/qemu-iotests/054.out

diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054
new file mode 100755
index 0000000..b360429
--- /dev/null
+++ b/tests/qemu-iotests/054
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Test huge qcow2 images
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+echo
+echo "creating too large image (1 EB)"
+_make_test_img $((1024*1024))T
+
+echo
+echo "creating too large image (1 EB) using qcow2.py"
+_make_test_img 4G
+./qcow2.py $TEST_IMG set-header size $((1024 ** 6))
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/054.out b/tests/qemu-iotests/054.out
new file mode 100644
index 0000000..0b2fe30
--- /dev/null
+++ b/tests/qemu-iotests/054.out
@@ -0,0 +1,10 @@
+QA output created by 054
+
+creating too large image (1 EB)
+qemu-img: The image size is too large for file format 'qcow2'
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 
+
+creating too large image (1 EB) using qcow2.py
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 
+qemu-img: Could not open 'TEST_DIR/t.qcow2': File too large
+*** done
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 31eb62b..e9ba358 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -167,7 +167,7 @@ _cleanup_test_img()
 
 _check_test_img()
 {
-    $QEMU_IMG check "$@" -f $IMGFMT $TEST_IMG 2>&1 | \
+    $QEMU_IMG check "$@" -f $IMGFMT $TEST_IMG 2>&1 | _filter_testdir | \
         sed -e '/allocated.*fragmented.*compressed clusters/d' \
             -e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \
             -e '/Image end offset: [0-9]\+/d'
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index bf944d9..387b050 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -60,3 +60,4 @@
 #051 rw auto
 052 rw auto backing
 053 rw auto
+054 rw auto
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows
  2013-05-13 13:42 [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Kevin Wolf
                   ` (2 preceding siblings ...)
  2013-05-13 13:42 ` [Qemu-devel] [PATCH 3/3] qemu-iotests: Try creating huge qcow2 image Kevin Wolf
@ 2013-05-13 14:39 ` Richard W.M. Jones
  3 siblings, 0 replies; 6+ messages in thread
From: Richard W.M. Jones @ 2013-05-13 14:39 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, stefanha

On Mon, May 13, 2013 at 03:42:53PM +0200, Kevin Wolf wrote:
[...]

I tested the first patch (didn't try the tests) with qemu-img
and libguestfs and it works.

New qemu-img fails with:
qemu-img: The image size is too large for file format 'qcow2'

If you use the old qemu-img to create the file, and then try
to add it with the new qemu, you get:

-drive file=/tmp/huge.qcow2,cache=none,id=hd0,if=none: could not open disk image /tmp/huge.qcow2: File too large

(instead of a segfault).

So:

ACK.

Tested-by: Richard W.M. Jones <rjones@redhat.com>

My only gripe is it would be nice if the qemu-img error message
mentioned that you can use the -o cluster_size=XXX option.

> Suggestions for a better test case are welcome. But now that creating a large
> image file fails, and if you somehow manage to create it anyway (qcow2.py)
> opening it fails, it's hard to test the actual read/write cases.

It'd be nice if the test included a test of the huge case using -o
cluster_size=2M and a few reads and writes at the end of the disk,
just to make sure this doesn't break in future.

Also nice to have would be to be able to specify disk sizes using 'P'
and 'E' :-)

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine.  Supports Linux and Windows.
http://people.redhat.com/~rjones/virt-df/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Qemu-devel] [PATCH 2/3] qcow2.py: Subcommand for changing header fields
  2013-05-13 13:42 ` [Qemu-devel] [PATCH 2/3] qcow2.py: Subcommand for changing header fields Kevin Wolf
@ 2013-05-13 14:59   ` Stefan Hajnoczi
  0 siblings, 0 replies; 6+ messages in thread
From: Stefan Hajnoczi @ 2013-05-13 14:59 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, rjones

On Mon, May 13, 2013 at 03:42:55PM +0200, Kevin Wolf wrote:
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  tests/qemu-iotests/qcow2.py | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py
> index fecf5b9..b6abd16 100755
> --- a/tests/qemu-iotests/qcow2.py
> +++ b/tests/qemu-iotests/qcow2.py
> @@ -149,6 +149,17 @@ def cmd_dump_header(fd):
>      h.dump()
>      h.dump_extensions()
>  
> +def cmd_set_header(fd, name, value):
> +    try:
> +        value = int(value, 0)
> +    except:
> +        print "'%s' is not a valid number" % value
> +        sys.exit(1)
> +
> +    h = QcowHeader(fd)
> +    h.__dict__[name] = value
> +    h.update(fd)

No error checking on 'name'.  Since end-users are unlikely to try
qcow2.py we can get away with this, but it might save some poor person
time in the future if we check "name in QcowHeader.fields" and print an
error.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2013-05-13 15:01 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-05-13 13:42 [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Kevin Wolf
2013-05-13 13:42 ` [Qemu-devel] [PATCH for-1.5 1/3] " Kevin Wolf
2013-05-13 13:42 ` [Qemu-devel] [PATCH 2/3] qcow2.py: Subcommand for changing header fields Kevin Wolf
2013-05-13 14:59   ` Stefan Hajnoczi
2013-05-13 13:42 ` [Qemu-devel] [PATCH 3/3] qemu-iotests: Try creating huge qcow2 image Kevin Wolf
2013-05-13 14:39 ` [Qemu-devel] [PATCH for-1.5 0/3] qcow2: Catch some L1 table index overflows Richard W.M. Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).