Commit e0d9c6f9 authored by Chunqiang Tang's avatar Chunqiang Tang Committed by Kevin Wolf
Browse files

QCOW2: bug fix - read base image beyond its size



This patch fixes the following bug in QCOW2. For a QCOW2 image that is larger
than its base image, when handling a read request straddling over the end of the
base image, the QCOW2 driver attempts to read beyond the end of the base image
and the request would fail.

This bug was found by Fast Virtual Disk (FVD)'s fully automated testing tool.
The following test triggered the bug.

dd if=/dev/zero of=/var/ramdisk/truth.raw count=0 bs=1 seek=1098561536
dd if=/dev/zero of=/var/ramdisk/zero-500M.raw count=0 bs=1 seek=593099264
./qemu-img create -f qcow2 -ocluster_size=65536,backing_fmt=blksim -b /var/ramdisk/zero-500M.raw /var/ramdisk/test.qcow2 1098561536
./qemu-io --auto --seed=30477694 --truth=/var/ramdisk/truth.raw --format=qcow2 --test=blksim:/var/ramdisk/test.qcow2 --verify_write=true --compare_before=false --compare_after=true --round=100000 --parallel=100 --io_size=10485760 --fail_prob=0 --cancel_prob=0 --instant_qemubh=true

Signed-off-by: default avatarChunqiang Tang <ctang@us.ibm.com>
Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 982aa955
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -355,7 +355,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
    else
        n1 = bs->total_sectors - sector_num;

    qemu_iovec_memset(qiov, 0, 512 * (nb_sectors - n1));
    qemu_iovec_memset_skip(qiov, 0, 512 * (nb_sectors - n1), 512 * n1);

    return n1;
}
@@ -478,8 +478,7 @@ static void qcow2_aio_read_cb(void *opaque, int ret)
            if (n1 > 0) {
                BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
                acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
                                    &acb->hd_qiov, acb->cur_nr_sectors,
				    qcow2_aio_read_cb, acb);
                                    &acb->hd_qiov, n1, qcow2_aio_read_cb, acb);
                if (acb->hd_aiocb == NULL)
                    goto done;
            } else {
+31 −0
Original line number Diff line number Diff line
@@ -267,6 +267,37 @@ void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count)
    }
}

void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
                            size_t skip)
{
    int i;
    size_t done;
    void *iov_base;
    uint64_t iov_len;

    done = 0;
    for (i = 0; (i < qiov->niov) && (done != count); i++) {
        if (skip >= qiov->iov[i].iov_len) {
            /* Skip the whole iov */
            skip -= qiov->iov[i].iov_len;
            continue;
        } else {
            /* Skip only part (or nothing) of the iov */
            iov_base = (uint8_t*) qiov->iov[i].iov_base + skip;
            iov_len = qiov->iov[i].iov_len - skip;
            skip = 0;
        }

        if (done + iov_len > count) {
            memset(iov_base, c, count - done);
            break;
        } else {
            memset(iov_base, c, iov_len);
        }
        done += iov_len;
    }
}

#ifndef _WIN32
/* Sets a specific flag */
int fcntl_setfl(int fd, int flag)
+2 −0
Original line number Diff line number Diff line
@@ -322,6 +322,8 @@ void qemu_iovec_reset(QEMUIOVector *qiov);
void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf);
void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count);
void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count);
void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
                            size_t skip);

struct Monitor;
typedef struct Monitor Monitor;