Commit da398fcc authored by Peter Maydell's avatar Peter Maydell

Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging

Block pull request

# gpg: Signature made Fri 15 Aug 2014 18:04:23 BST using RSA key ID 81AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>"
# gpg:                 aka "Stefan Hajnoczi <stefanha@gmail.com>"

* remotes/stefanha/tags/block-pull-request: (55 commits)
  qcow2: fix new_blocks double-free in alloc_refcount_block()
  image-fuzzer: Reduce number of generator functions in __init__
  image-fuzzer: Add generators of L1/L2 tables
  image-fuzzer: Add fuzzing functions for L1/L2 table entries
  docs: Expand the list of supported image elements with L1/L2 tables
  image-fuzzer: Public API for image-fuzzer/runner/runner.py
  image-fuzzer: Generator of fuzzed qcow2 images
  image-fuzzer: Fuzzing functions for qcow2 images
  image-fuzzer: Tool for fuzz tests execution
  docs: Specification for the image fuzzer
  ide: only constrain read/write requests to drive size, not other types
  virtio-blk: Correct bug in support for flexible descriptor layout
  libqos: Change free function called in malloc
  libqos: Correct mask to align size to PAGE_SIZE in malloc-pc
  libqtest: add QTEST_LOG for debugging qtest testcases
  ide: Fix segfault when flushing a device that doesn't exist
  qemu-options: add missing -drive discard option to cmdline help
  parallels: 2TB+ parallels images support
  parallels: split check for parallels format in parallels_open
  parallels: replace tabs with spaces in block/parallels.c
  ...
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 142f4ac5 39ba3bf6
......@@ -522,6 +522,25 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
}
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
if (rule->options.inject.sector == -1) {
break;
}
}
if (rule && rule->options.inject.error) {
return inject_error(bs, cb, opaque, rule);
}
return bdrv_aio_flush(bs->file, cb, opaque);
}
static void blkdebug_close(BlockDriverState *bs)
{
......@@ -699,6 +718,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_aio_readv = blkdebug_aio_readv,
.bdrv_aio_writev = blkdebug_aio_writev,
.bdrv_aio_flush = blkdebug_aio_flush,
.bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
......
......@@ -30,6 +30,7 @@
/**************************************************************/
#define HEADER_MAGIC "WithoutFreeSpace"
#define HEADER_MAGIC2 "WithouFreSpacExt"
#define HEADER_VERSION 2
#define HEADER_SIZE 64
......@@ -41,8 +42,10 @@ struct parallels_header {
uint32_t cylinders;
uint32_t tracks;
uint32_t catalog_entries;
uint32_t nb_sectors;
char padding[24];
uint64_t nb_sectors;
uint32_t inuse;
uint32_t data_off;
char padding[12];
} QEMU_PACKED;
typedef struct BDRVParallelsState {
......@@ -52,6 +55,8 @@ typedef struct BDRVParallelsState {
unsigned int catalog_size;
unsigned int tracks;
unsigned int off_multiplier;
} BDRVParallelsState;
static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
......@@ -59,11 +64,12 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
const struct parallels_header *ph = (const void *)buf;
if (buf_size < HEADER_SIZE)
return 0;
return 0;
if (!memcmp(ph->magic, HEADER_MAGIC, 16) &&
(le32_to_cpu(ph->version) == HEADER_VERSION))
return 100;
if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
!memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
(le32_to_cpu(ph->version) == HEADER_VERSION))
return 100;
return 0;
}
......@@ -83,14 +89,19 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
error_setg(errp, "Image not in Parallels format");
ret = -EINVAL;
goto fail;
}
bs->total_sectors = le64_to_cpu(ph.nb_sectors);
bs->total_sectors = le32_to_cpu(ph.nb_sectors);
if (le32_to_cpu(ph.version) != HEADER_VERSION) {
goto fail_format;
}
if (!memcmp(ph.magic, HEADER_MAGIC, 16)) {
s->off_multiplier = 1;
bs->total_sectors = 0xffffffff & bs->total_sectors;
} else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) {
s->off_multiplier = le32_to_cpu(ph.tracks);
} else {
goto fail_format;
}
s->tracks = le32_to_cpu(ph.tracks);
if (s->tracks == 0) {
......@@ -98,6 +109,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL;
goto fail;
}
if (s->tracks > INT32_MAX/513) {
error_setg(errp, "Invalid image: Too big cluster");
ret = -EFBIG;
goto fail;
}
s->catalog_size = le32_to_cpu(ph.catalog_entries);
if (s->catalog_size > INT_MAX / 4) {
......@@ -117,11 +133,14 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
}
for (i = 0; i < s->catalog_size; i++)
le32_to_cpus(&s->catalog_bitmap[i]);
le32_to_cpus(&s->catalog_bitmap[i]);
qemu_co_mutex_init(&s->lock);
return 0;
fail_format:
error_setg(errp, "Image not in Parallels format");
ret = -EINVAL;
fail:
g_free(s->catalog_bitmap);
return ret;
......@@ -137,8 +156,9 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
/* not allocated */
if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
return -1;
return (uint64_t)(s->catalog_bitmap[index] + offset) * 512;
return -1;
return
((uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset) * 512;
}
static int parallels_read(BlockDriverState *bs, int64_t sector_num,
......
......@@ -381,6 +381,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks,
blocks_clusters * s->cluster_size);
g_free(new_blocks);
new_blocks = NULL;
if (ret < 0) {
goto fail_table;
}
......
# Specification for the fuzz testing tool
#
# Copyright (C) 2014 Maria Kustova <maria.k@catit.be>
#
# 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/>.
Image fuzzer
============
Description
-----------
The goal of the image fuzzer is to catch crashes of qemu-io/qemu-img
by providing to them randomly corrupted images.
Test images are generated from scratch and have valid inner structure with some
elements, e.g. L1/L2 tables, having random invalid values.
Test runner
-----------
The test runner generates test images, executes tests utilizing generated
images, indicates their results and collects all test related artifacts (logs,
core dumps, test images, backing files).
The test means execution of all available commands under test with the same
generated test image.
By default, the test runner generates new tests and executes them until
keyboard interruption. But if a test seed is specified via the '--seed' runner
parameter, then only one test with this seed will be executed, after its finish
the runner will exit.
The runner uses an external image fuzzer to generate test images. An image
generator should be specified as a mandatory parameter of the test runner.
Details about interactions between the runner and fuzzers see "Module
interfaces".
The runner activates generation of core dumps during test executions, but it
assumes that core dumps will be generated in the current working directory.
For comprehensive test results, please, set up your test environment
properly.
Paths to binaries under test (SUTs) qemu-img and qemu-io are retrieved from
environment variables. If the environment check fails the runner will
use SUTs installed in system paths.
qemu-img is required for creation of backing files, so it's mandatory to set
the related environment variable if it's not installed in the system path.
For details about environment variables see qemu-iotests/check.
The runner accepts a JSON array of fields expected to be fuzzed via the
'--config' argument, e.g.
'[["feature_name_table"], ["header", "l1_table_offset"]]'
Each sublist can have one or two strings defining image structure elements.
In the latter case a parent element should be placed on the first position,
and a field name on the second one.
The runner accepts a list of commands under test as a JSON array via
the '--command' argument. Each command is a list containing a SUT and all its
arguments, e.g.
runner.py -c '[["qemu-io", "$test_img", "-c", "write $off $len"]]'
/tmp/test ../qcow2
For variable arguments next aliases can be used:
- $test_img for a fuzzed img
- $off for an offset in the fuzzed image
- $len for a data size
Values for last two aliases will be generated based on a size of a virtual
disk of the generated image.
In case when no commands are specified the runner will execute commands from
the default list:
- qemu-img check
- qemu-img info
- qemu-img convert
- qemu-io -c read
- qemu-io -c write
- qemu-io -c aio_read
- qemu-io -c aio_write
- qemu-io -c flush
- qemu-io -c discard
- qemu-io -c truncate
Qcow2 image generator
---------------------
The 'qcow2' generator is a Python package providing 'create_image' method as
a single public API. See details in 'Test runner/image fuzzer' chapter of
'Module interfaces'.
Qcow2 contains two submodules: fuzz.py and layout.py.
'fuzz.py' contains all fuzzing functions, one per image field. It's assumed
that after code analysis every field will have own constraints for its value.
For now only universal potentially dangerous values are used, e.g. type limits
for integers or unsafe symbols as '%s' for strings. For bitmasks random amount
of bits are set to ones. All fuzzed values are checked on non-equality to the
current valid value of the field. In case of equality the value will be
regenerated.
'layout.py' creates a random valid image, fuzzes a random subset of the image
fields by 'fuzz.py' module and writes a fuzzed image to the file specified.
If a fuzzer configuration is specified, then it has the next interpretation:
1. If a list contains a parent image element only, then some random portion
of fields of this element will be fuzzed every test.
The same behavior is applied for the entire image if no configuration is
used. This case is useful for the test specialization.
2. If a list contains a parent element and a field name, then a field
will be always fuzzed for every test. This case is useful for regression
testing.
For now only header fields, header extensions and L1/L2 tables are generated.
Module interfaces
-----------------
* Test runner/image fuzzer
The runner calls an image generator specifying the path to a test image file,
path to a backing file and its format and a fuzzer configuration.
An image generator is expected to provide a
'create_image(test_img_path, backing_file_path=None,
backing_file_format=None, fuzz_config=None)'
method that creates a test image, writes it to the specified file and returns
the size of the virtual disk.
The file should be created if it doesn't exist or overwritten otherwise.
fuzz_config has a form of a list of lists. Every sublist can have one
or two elements: first element is a name of a parent image element, second one
if exists is a name of a field in this element.
Example,
[['header', 'l1_table_offset'],
['header', 'nb_snapshots'],
['feature_name_table']]
Random seed is set by the runner at every test execution for the regression
purpose, so an image generator is not recommended to modify it internally.
Overall fuzzer requirements
===========================
Input data:
----------
- image template (generator)
- work directory
- action vector (optional)
- seed (optional)
- SUT and its arguments (optional)
Fuzzer requirements:
-------------------
1. Should be able to inject random data
2. Should be able to select a random value from the manually pregenerated
vector (boundary values, e.g. max/min cluster size)
3. Image template should describe a general structure invariant for all
test images (image format description)
4. Image template should be autonomous and other fuzzer parts should not
rely on it
5. Image template should contain reference rules (not only block+size
description)
6. Should generate the test image with the correct structure based on an image
template
7. Should accept a seed as an argument (for regression purpose)
8. Should generate a seed if it is not specified as an input parameter.
9. The same seed should generate the same image for the same action vector,
specified or generated.
10. Should accept a vector of actions as an argument (for test reproducing and
for test case specification, e.g. group of tests for header structure,
group of test for snapshots, etc)
11. Action vector should be randomly generated from the pool of available
actions, if it is not specified as an input parameter
12. Pool of actions should be defined automatically based on an image template
13. Should accept a SUT and its call parameters as an argument or select them
randomly otherwise. As far as it's expected to be rarely changed, the list
of all possible test commands can be available in the test runner
internally.
14. Should support an external cancellation of a test run
15. Seed should be logged (for regression purpose)
16. All files related to a test result should be collected: a test image,
SUT logs, fuzzer logs and crash dumps
17. Should be compatible with python version 2.4-2.7
18. Usage of external libraries should be limited as much as possible.
Image formats:
-------------
Main target image format is qcow2, but support of image templates should
provide an ability to add any other image format.
Effectiveness:
-------------
The fuzzer can be controlled via template, seed and action vector;
it makes the fuzzer itself invariant to an image format and test logic.
It should be able to perform rather complex and precise tests, that can be
specified via an action vector. Otherwise, knowledge about an image structure
allows the fuzzer to generate the pool of all available areas can be fuzzed
and randomly select some of them and so compose its own action vector.
Also complexity of a template defines complexity of the fuzzer, so its
functionality can be varied from simple model-independent fuzzing to smart
model-based one.
Glossary:
--------
Action vector is a sequence of structure elements retrieved from an image
format, each of them will be fuzzed for the test image. It's a subset of
elements of the action pool. Example: header, refcount table, etc.
Action pool is all available elements of an image structure that generated
automatically from an image template.
Image template is a formal description of an image structure and relations
between image blocks.
Test image is an output image of the fuzzer defined by the current seed and
action vector.
......@@ -28,6 +28,7 @@ struct VirtIOBlockDataPlane {
bool started;
bool starting;
bool stopping;
bool disabled;
VirtIOBlkConf *blk;
......@@ -218,8 +219,9 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
VirtQueue *vq;
int r;
if (s->started) {
if (s->started || s->disabled) {
return;
}
......@@ -231,22 +233,23 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
vq = virtio_get_queue(s->vdev, 0);
if (!vring_setup(&s->vring, s->vdev, 0)) {
s->starting = false;
return;
goto fail_vring;
}
/* Set up guest notifier (irq) */
if (k->set_guest_notifiers(qbus->parent, 1, true) != 0) {
fprintf(stderr, "virtio-blk failed to set guest notifier, "
"ensure -enable-kvm is set\n");
exit(1);
r = k->set_guest_notifiers(qbus->parent, 1, true);
if (r != 0) {
fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
"ensure -enable-kvm is set\n", r);
goto fail_guest_notifiers;
}
s->guest_notifier = virtio_queue_get_guest_notifier(vq);
/* Set up virtqueue notify */
if (k->set_host_notifier(qbus->parent, 0, true) != 0) {
fprintf(stderr, "virtio-blk failed to set host notifier\n");
exit(1);
r = k->set_host_notifier(qbus->parent, 0, true);
if (r != 0) {
fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
goto fail_host_notifier;
}
s->host_notifier = *virtio_queue_get_host_notifier(vq);
......@@ -266,6 +269,15 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
aio_context_acquire(s->ctx);
aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify);
aio_context_release(s->ctx);
return;
fail_host_notifier:
k->set_guest_notifiers(qbus->parent, 1, false);
fail_guest_notifiers:
vring_teardown(&s->vring, s->vdev, 0);
s->disabled = true;
fail_vring:
s->starting = false;
}
/* Context: QEMU global mutex held */
......@@ -274,6 +286,13 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
/* Better luck next time. */
if (s->disabled) {
s->disabled = false;
return;
}
if (!s->started || s->stopping) {
return;
}
......
......@@ -404,19 +404,19 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
* NB: per existing s/n string convention the string is
* terminated by '\0' only when shorter than buffer.
*/
strncpy(req->elem.in_sg[0].iov_base,
s->blk.serial ? s->blk.serial : "",
MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
const char *serial = s->blk.serial ? s->blk.serial : "";
size_t size = MIN(strlen(serial) + 1,
MIN(iov_size(in_iov, in_num),
VIRTIO_BLK_ID_BYTES));
iov_from_buf(in_iov, in_num, 0, serial, size);
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
virtio_blk_free_request(req);
} else if (type & VIRTIO_BLK_T_OUT) {
qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
req->elem.out_num - 1);
qemu_iovec_init_external(&req->qiov, iov, out_num);
virtio_blk_handle_write(req, mrb);
} else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) {
/* VIRTIO_BLK_T_IN is 0, so we can't just & it. */
qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
req->elem.in_num - 1);
qemu_iovec_init_external(&req->qiov, in_iov, in_num);
virtio_blk_handle_read(req);
} else {
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
......
......@@ -234,7 +234,7 @@ static void pc_q35_init(MachineState *machine)
gsi_state->i8259_irq[i] = i8259[i];
}
if (pci_enabled) {
ioapic_init_gsi(gsi_state, NULL);
ioapic_init_gsi(gsi_state, "q35");
}
qdev_init_nofail(icc_bridge);
......
......@@ -584,7 +584,72 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
s->dev[port].finished |= finished;
*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished);
ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_STAT_SDBS);
ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_SDB_FIS);
}
static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
{
AHCIPortRegs *pr = &ad->port_regs;
uint8_t *pio_fis, *cmd_fis;
uint64_t tbl_addr;
dma_addr_t cmd_len = 0x80;
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
return;
}
/* map cmd_fis */
tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr);
cmd_fis = dma_memory_map(ad->hba->as, tbl_addr, &cmd_len,
DMA_DIRECTION_TO_DEVICE);
if (cmd_fis == NULL) {
DPRINTF(ad->port_no, "dma_memory_map failed in ahci_write_fis_pio");
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR);
return;
}
if (cmd_len != 0x80) {
DPRINTF(ad->port_no,
"dma_memory_map mapped too few bytes in ahci_write_fis_pio");
dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
DMA_DIRECTION_TO_DEVICE, cmd_len);
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR);
return;
}
pio_fis = &ad->res_fis[RES_FIS_PSFIS];
pio_fis[0] = 0x5f;
pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
pio_fis[2] = ad->port.ifs[0].status;
pio_fis[3] = ad->port.ifs[0].error;
pio_fis[4] = cmd_fis[4];
pio_fis[5] = cmd_fis[5];
pio_fis[6] = cmd_fis[6];
pio_fis[7] = cmd_fis[7];
pio_fis[8] = cmd_fis[8];
pio_fis[9] = cmd_fis[9];
pio_fis[10] = cmd_fis[10];
pio_fis[11] = cmd_fis[11];
pio_fis[12] = cmd_fis[12];
pio_fis[13] = cmd_fis[13];
pio_fis[14] = 0;
pio_fis[15] = ad->port.ifs[0].status;
pio_fis[16] = len & 255;
pio_fis[17] = len >> 8;
pio_fis[18] = 0;
pio_fis[19] = 0;
if (pio_fis[2] & ERR_STAT) {
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR);
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS);
dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
DMA_DIRECTION_TO_DEVICE, cmd_len);
}
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
......@@ -629,7 +694,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
}
if (d2h_fis[2] & ERR_STAT) {
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_TFES);
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR);
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
......@@ -969,11 +1034,6 @@ static int handle_cmd(AHCIState *s, int port, int slot)
/* We're ready to process the command in FIS byte 2. */
ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
if ((s->dev[port].port.ifs[0].status & (READY_STAT|DRQ_STAT|BUSY_STAT)) ==
READY_STAT) {
ahci_write_fis_d2h(&s->dev[port], cmd_fis);
}
}
out:
......@@ -991,7 +1051,7 @@ out:
}
/* DMA dev <-> ram */
static int ahci_start_transfer(IDEDMA *dma)
static void ahci_start_transfer(IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
......@@ -1038,11 +1098,9 @@ out:
s->end_transfer_func(s);
if (!(s->status & DRQ_STAT)) {
/* done with DMA */
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS);
/* done with PIO send/receive */
ahci_w</