Bug 1613215 - qemu-img shouldn't truncate image with the invalid option value
Summary: qemu-img shouldn't truncate image with the invalid option value
Keywords:
Status: CLOSED DUPLICATE of bug 1654639
Alias: None
Product: Red Hat Enterprise Linux Advanced Virtualization
Classification: Red Hat
Component: qemu-kvm
Version: ---
Hardware: Unspecified
OS: Unspecified
low
low
Target Milestone: rc
: ---
Assignee: Virtualization Maintenance
QA Contact: Virtualization Bugs
URL:
Whiteboard:
Depends On:
Blocks: 1649160 1654639
TreeView+ depends on / blocked
 
Reported: 2018-08-07 08:56 UTC by Tingting Mao
Modified: 2021-12-17 17:22 UTC (History)
11 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
: 1654639 (view as bug list)
Environment:
Last Closed: 2019-02-01 17:02:51 UTC
Type: Bug
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)

Description Tingting Mao 2018-08-07 08:56:18 UTC
Description of problem:
Qemu-img should create no file when the value of supported option is not correct.

Version-Release number of selected component (if applicable):
qemu-kvm-rhev-2.12.0-9.el7
kernel-3.10.0-931.el7

How reproducible:
100%

Steps to Reproduce:
1.Create qcow2 image with unsupported value of option
# qemu-img create -f qcow2 -o preallocation=xxxxtt test02.qcow2 1G
Formatting 'test02.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 preallocation=xxxxtt lazy_refcounts=off refcount_bits=16
qemu-img: test02.qcow2: Invalid parameter 'xxxxtt'
# qemu-img create -f qcow2 -o cluster_size=0 test03.qcow2 1G
Formatting 'test03.qcow2', fmt=qcow2 size=1073741824 cluster_size=0 lazy_refcounts=off refcount_bits=16
qemu-img: test03.qcow2: Cluster size must be a power of two between 512 and 2048k
2.Check the info of the images created in step1
# qemu-img info test02.qcow2 
image: test02.qcow2
file format: raw
virtual size: 0 (0 bytes)
disk size: 0
# qemu-img info test03.qcow2 
image: test03.qcow2
file format: raw
virtual size: 0 (0 bytes)
disk size: 0


Actual results:

Expected results:
There should be no images when specifying the wrong value of the option.


Additional info:
The result is as expected for raw image[1], and there is no file in qemu-kvm-rhev-2.10.0[2]. So I mark this issue as regression.

[1]# qemu-img create -f raw -o preallocation=tttxxx test03.img 1G
Formatting 'test03.img', fmt=raw size=1073741824 preallocation=tttxxx
qemu-img: test03.img: invalid parameter value: tttxxx
# qemu-img info test03.img
qemu-img: Could not open 'test03.img': Could not open 'test03.img': No such file or directory

[2]# qemu-img --version
qemu-img version 2.10.0(qemu-kvm-rhev-2.10.0-21.el7_5.1)
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers
# qemu-img create -f qcow2 -o preallocation=xxxxtt test02.qcow2 1G
Formatting 'test02.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 preallocation=xxxxtt lazy_refcounts=off refcount_bits=16
qemu-img: test02.qcow2: invalid parameter value: xxxxtt
# qemu-img info test02.qcow2
qemu-img: Could not open 'test02.qcow2': Could not open 'test02.qcow2': No such file or directory

Comment 3 Ping Li 2018-08-08 16:50:45 UTC
(In reply to timao from comment #0)
> Steps to Reproduce:
> 1.Create qcow2 image with unsupported value of option
> # qemu-img create -f qcow2 -o preallocation=xxxxtt test02.qcow2 1G
> Formatting 'test02.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536
> preallocation=xxxxtt lazy_refcounts=off refcount_bits=16
> qemu-img: test02.qcow2: Invalid parameter 'xxxxtt'

When create a qcow2 image, function qemu_opts_to_qdict_filtered() is called to convert QemuOpts object to QDict. In the commit b76b4f60452, boolean variable del is set as true which means the options passed to qemu-img create will be removed from QemuOpts once they are added to QDict. 
In the function raw_co_create_opts() as follow, QEMU tries to get the preallocation mode specified by the user from the object QemuOpts. But there is nothing. buf will be always NULL. Then QEMU will not abort even if the user gives an invalid preallocation mode and truncate a image with zero bytes. 

    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
                               PREALLOC_MODE_OFF, &local_err);
    g_free(buf);
    if (local_err) {
        error_propagate(errp, local_err);
        return -EINVAL;
    }

When disabled the flag to don't remove options from QemuOpts, QEMU would work as expect(Give an error message and don't truncate the image)

# git diff 
diff --git a/block/qcow2.c b/block/qcow2.c
index 1ea7203..e9ea5fe 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3059,7 +3059,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOp
      * options meant for the protocol layer so that the visitor doesn't
      * complain. */
     qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
-                                        true);
+                                        false);
 
     /* Handle encryption options */
     val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);

 # /root/qemu-kvm/qemu-img create -f qcow2 -o preallocation=mode test02.qcow2 100M
Formatting 'test02.qcow2', fmt=qcow2 size=104857600 cluster_size=65536 preallocation=mode lazy_refcounts=off refcount_bits=16
qemu-img: test02.qcow2: invalid parameter value: mode
# ls -lsh test02.qcow2
ls: cannot access test02.qcow2: No such file or directory

> # qemu-img create -f qcow2 -o cluster_size=0 test03.qcow2 1G
> Formatting 'test03.qcow2', fmt=qcow2 size=1073741824 cluster_size=0
> lazy_refcounts=off refcount_bits=16
> qemu-img: test03.qcow2: Cluster size must be a power of two between 512 and
> 2048k

Below check code was removed in the commit b76b4f60452
    cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        ret = -EINVAL;
        goto finish;
    }

With below code, image would not be truncated with invalid cluster size. However, I don't think the check should be moved back. Create qcow2 image is not a atomic operation. QEMU truncates a image, allocates the qcow2 header and fill in the data, then create metadata and allocates space if needed. Also the image created whose virtual size is 0. It is harmless as user would be easy to find that it is useless. So I think it is not important whether the empty image is created.
 
# git diff 
diff --git a/block/qcow2.c b/block/qcow2.c
index 1ea7203..5692e0e 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3059,7 +3059,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOp
      * options meant for the protocol layer so that the visitor doesn't
      * complain. */
     qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
-                                        true);
+                                        false);
 
     /* Handle encryption options */
     val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
@@ -3100,6 +3100,12 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuO
         goto finish;
     }
 
+    qcow2_opt_get_cluster_size_del(opts, &local_err);
+    if (local_err) {
+        ret = -EINVAL;
+        goto finish;
+    }
+
     /* Create and open the file (protocol layer) */
     ret = bdrv_create_file(filename, opts, errp);
     if (ret < 0) {

# /root/qemu-kvm/qemu-img create -f qcow2 -o cluster_size=0 test03.qcow2 1G
Formatting 'test03.qcow2', fmt=qcow2 size=1073741824 cluster_size=0 lazy_refcounts=off refcount_bits=16
qemu-img: test03.qcow2: Could not create image: Invalid argument
# ls -lsh test03.qcow2
ls: cannot access test03.qcow2: No such file or directory


> 2.Check the info of the images created in step1
> # qemu-img info test02.qcow2 
> image: test02.qcow2
> file format: raw
> virtual size: 0 (0 bytes)
> disk size: 0
> # qemu-img info test03.qcow2 
> image: test03.qcow2
> file format: raw
> virtual size: 0 (0 bytes)
> disk size: 0

Additional info:
Breakpoint 1, qcow2_co_create_opts (filename=0x555556506200 "test02.qcow2", 
    opts=0x555556536000, errp=0x7ffff7fc5f90) at block/qcow2.c:3061
3061	    qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
(gdb) p *opts
$3 = {id = 0x0, list = 0x55555654e000, loc = {kind = LOC_NONE, num = 0, ptr = 0x0, 
    prev = 0x0}, head = {tqh_first = 0x5555565002c0, tqh_last = 0x555556500328}, next = {
    tqe_next = 0x0, tqe_prev = 0x55555654e018}}
(gdb) p *(QemuOpt *) 0x5555565002c0
$4 = {name = 0x555556524078 "size", str = 0x5555565061e0 "104857600", desc = 0x55555654e028, 
  value = {boolean = false, uint = 104857600}, opts = 0x555556536000, next = {
    tqe_next = 0x555556500300, tqe_prev = 0x555556536028}}
(gdb) p *(QemuOpt *) 0x555556500300
$5 = {name = 0x5555565061f0 "preallocation", str = 0x555556524080 "mode", 
  desc = 0x55555654e1e8, value = {boolean = false, uint = 0}, opts = 0x555556536000, next = {
    tqe_next = 0x0, tqe_prev = 0x5555565002e8}}
(gdb) next
3061	    qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
(gdb) next
3065	    val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
(gdb) p *opts
$6 = {id = 0x0, list = 0x55555654e000, loc = {kind = LOC_NONE, num = 0, ptr = 0x0, 
    prev = 0x0}, head = {tqh_first = 0x0, tqh_last = 0x555556536028}, next = {tqe_next = 0x0, 
    tqe_prev = 0x55555654e018}}
(gdb) c
Continuing.

Breakpoint 2, raw_co_create_opts (filename=0x5555565061f0 "test02.qcow2", 
    opts=0x555556536000, errp=0x7ffff7fc5e90) at block/file-posix.c:2228
2228	    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
(gdb) next
2227	    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
(gdb) next
2228	    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
(gdb) p buf
$8 = 0x0
(gdb) next
2230	    g_free(buf);
(gdb) p local_err
$9 = (Error *) 0x0

Comment 4 Ping Li 2018-08-09 08:08:49 UTC
After a discussion with Kevin. The original QemuOpts mixes options for the format and for the protocol. What qemu_opts_to_qdict_filtered() does is to split them into the qcow2 part (in the QDict) and the file part (which remains in the QemuOpts). In addition, everything that qcow2 doesn't understand, is assumed to be a option for file.
So the remaining problem is the image was created for invalid options. However blockdev-create requires you to have a fully opened image file before it even begins to check options etc. So the file must already exist. 
As the truncated image is unusable(zero bytes and in raw format, not qocw2), low the priority of the bug. Thanks.

Comment 9 Ademar Reis 2019-02-01 17:02:51 UTC

*** This bug has been marked as a duplicate of bug 1654639 ***


Note You need to log in before you can comment on or make changes to this bug.