diff --git a/block.c b/block.c index 809ec54587..35f7a0a0ab 100644 --- a/block.c +++ b/block.c @@ -662,8 +662,8 @@ BlockDriver *bdrv_find_protocol(const char *filename, * probing score. * Return the first block driver with the highest probing score. */ -static BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, - const char *filename) +BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, + const char *filename) { int score_max = 0, score; BlockDriver *drv = NULL, *d; @@ -1489,6 +1489,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } /* Image format probing */ + bs->probed = !drv; if (!drv && file) { ret = find_image_format(file, filename, &drv, &local_err); if (ret < 0) { diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 401b967e85..2ce5409044 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -58,8 +58,58 @@ static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { + void *buf = NULL; + BlockDriver *drv; + QEMUIOVector local_qiov; + int ret; + + if (bs->probed && sector_num == 0) { + /* As long as these conditions are true, we can't get partial writes to + * the probe buffer and can just directly check the request. */ + QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512); + QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512); + + if (nb_sectors == 0) { + /* qemu_iovec_to_buf() would fail, but we want to return success + * instead of -EINVAL in this case. */ + return 0; + } + + buf = qemu_try_blockalign(bs->file, 512); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + ret = qemu_iovec_to_buf(qiov, 0, buf, 512); + if (ret != 512) { + ret = -EINVAL; + goto fail; + } + + drv = bdrv_probe_all(buf, 512, NULL); + if (drv != bs->drv) { + ret = -EPERM; + goto fail; + } + + /* Use the checked buffer, a malicious guest might be overwriting its + * original buffer in the background. */ + qemu_iovec_init(&local_qiov, qiov->niov + 1); + qemu_iovec_add(&local_qiov, buf, 512); + qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); + qiov = &local_qiov; + } + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + ret = bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov); + +fail: + if (qiov == &local_qiov) { + qemu_iovec_destroy(&local_qiov); + } + qemu_vfree(buf); + return ret; } static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, @@ -158,6 +208,18 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { bs->sg = bs->file->sg; + + if (bs->probed && !bdrv_is_read_only(bs)) { + fprintf(stderr, + "WARNING: Image format was not specified for '%s' and probing " + "guessed raw.\n" + " Automatically detecting the format is dangerous for " + "raw images, write operations on block 0 will be restricted.\n" + " Specify the 'raw' format explicitly to remove the " + "restrictions.\n", + bs->file->filename); + } + return 0; } diff --git a/include/block/block_int.h b/include/block/block_int.h index cd94559a64..192fce8760 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -326,6 +326,7 @@ struct BlockDriverState { int sg; /* if true, the device is a /dev/sg* */ int copy_on_read; /* if true, copy read backing sectors into image note this is a reference count */ + bool probed; BlockDriver *drv; /* NULL means no media */ void *opaque; @@ -414,6 +415,8 @@ struct BlockDriverState { }; int get_tmp_filename(char *filename, int size); +BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, + const char *filename); void bdrv_set_io_limits(BlockDriverState *bs, ThrottleConfig *cfg);