Block patches for 3.0-rc0:

- qcow2 metadata overlap protection for the persistent bitmap directory
 - Various bug fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJbQ69uAAoJEPQH2wBh1c9A9gkH/0veRUeFS0BwPnE3+NKHw2tu
 67RFyHi+HpwQf9nFjEeP9L2NqTQEnNMMZdpyB2DlLdEj1XCGgP1WCx6G46FI4erS
 jRGJufZA01b3Ud+kSR9LrzQFREaX/RslF1J27eMkv2O2FwFhJRPSH+UT/1WLoYJU
 2AroQ4BMO0MoIGQ/O4OPH8e9Z62p6HpbJuLPg52PU1+uj8vvXbALHHCKBD0VIu8p
 mvj+5aRnoem0ICYomrPoaw5fe6N9CXy5V3Cj7hlSgVA/CyBjBFmq0cA2ZD4020SY
 1Y6lOTm9PzBsEX4LnOLn2At1MzyPLERsTGee4zdn6r8g/0gUhNwfO7H2QV8NA5k=
 =0vHR
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/xanclic/tags/pull-block-2018-07-09' into staging

Block patches for 3.0-rc0:
- qcow2 metadata overlap protection for the persistent bitmap directory
- Various bug fixes

# gpg: Signature made Mon 09 Jul 2018 19:54:38 BST
# gpg:                using RSA key F407DB0061D5CF40
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>"
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1  1829 F407 DB00 61D5 CF40

* remotes/xanclic/tags/pull-block-2018-07-09:
  qcow2: add overlap check for bitmap directory
  iotests: Add VMDK backing file correlation test
  vmdk: Fix possible segfault with non-VMDK backing
  raw: Drop superfluous semicolon
  qcow2: Drop unreachable break
  file-posix: Fix fd_open check in raw_co_copy_range_to
  qcow2: Drop unused cluster_data

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-07-10 08:04:51 +01:00
commit 6784273a0e
11 changed files with 229 additions and 42 deletions

View File

@ -2611,7 +2611,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
}
src_s = src->bs->opaque;
if (fd_open(bs) < 0 || fd_open(bs) < 0) {
if (fd_open(src->bs) < 0 || fd_open(dst->bs) < 0) {
return -EIO;
}
return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset,

View File

@ -775,7 +775,12 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
}
}
ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size);
/* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not
* necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap
* directory in-place (actually, turn-off the extension), which is checked
* in qcow2_check_metadata_overlap() */
ret = qcow2_pre_write_overlap_check(
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size);
if (ret < 0) {
goto fail;
}

View File

@ -2705,6 +2705,16 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
}
}
if ((chk & QCOW2_OL_BITMAP_DIRECTORY) &&
(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS))
{
if (overlaps_with(s->bitmap_directory_offset,
s->bitmap_directory_size))
{
return QCOW2_OL_BITMAP_DIRECTORY;
}
}
return 0;
}

View File

@ -679,6 +679,11 @@ static QemuOptsList qcow2_runtime_opts = {
.type = QEMU_OPT_BOOL,
.help = "Check for unintended writes into an inactive L2 table",
},
{
.name = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
.type = QEMU_OPT_BOOL,
.help = "Check for unintended writes into the bitmap directory",
},
{
.name = QCOW2_OPT_CACHE_SIZE,
.type = QEMU_OPT_SIZE,
@ -712,14 +717,15 @@ static QemuOptsList qcow2_runtime_opts = {
};
static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
[QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER,
[QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1,
[QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2,
[QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
[QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
[QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1,
[QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
[QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER,
[QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1,
[QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2,
[QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
[QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
[QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1,
[QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
[QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
};
static void cache_clean_timer_cb(void *opaque)
@ -3299,7 +3305,6 @@ qcow2_co_copy_range_from(BlockDriverState *bs,
case QCOW2_CLUSTER_COMPRESSED:
ret = -ENOTSUP;
goto out;
break;
case QCOW2_CLUSTER_NORMAL:
child = bs->file;
@ -3345,7 +3350,6 @@ qcow2_co_copy_range_to(BlockDriverState *bs,
int ret;
unsigned int cur_bytes; /* number of sectors in current iteration */
uint64_t cluster_offset;
uint8_t *cluster_data = NULL;
QCowL2Meta *l2meta = NULL;
assert(!bs->encrypted);
@ -3404,7 +3408,6 @@ fail:
qemu_co_mutex_unlock(&s->lock);
qemu_vfree(cluster_data);
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
return ret;

View File

@ -94,6 +94,7 @@
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory"
#define QCOW2_OPT_CACHE_SIZE "cache-size"
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
@ -400,34 +401,36 @@ typedef enum QCow2ClusterType {
} QCow2ClusterType;
typedef enum QCow2MetadataOverlap {
QCOW2_OL_MAIN_HEADER_BITNR = 0,
QCOW2_OL_ACTIVE_L1_BITNR = 1,
QCOW2_OL_ACTIVE_L2_BITNR = 2,
QCOW2_OL_REFCOUNT_TABLE_BITNR = 3,
QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4,
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
QCOW2_OL_INACTIVE_L1_BITNR = 6,
QCOW2_OL_INACTIVE_L2_BITNR = 7,
QCOW2_OL_MAIN_HEADER_BITNR = 0,
QCOW2_OL_ACTIVE_L1_BITNR = 1,
QCOW2_OL_ACTIVE_L2_BITNR = 2,
QCOW2_OL_REFCOUNT_TABLE_BITNR = 3,
QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4,
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
QCOW2_OL_INACTIVE_L1_BITNR = 6,
QCOW2_OL_INACTIVE_L2_BITNR = 7,
QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8,
QCOW2_OL_MAX_BITNR = 8,
QCOW2_OL_MAX_BITNR = 9,
QCOW2_OL_NONE = 0,
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR),
QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR),
QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR),
QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR),
QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR),
QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR),
QCOW2_OL_NONE = 0,
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR),
QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR),
QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR),
QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR),
QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR),
QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR),
/* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
* reads. */
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR),
} QCow2MetadataOverlap;
/* Perform all overlap checks which can be done in constant time */
#define QCOW2_OL_CONSTANT \
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
QCOW2_OL_SNAPSHOT_TABLE)
QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY)
/* Perform all overlap checks which don't require disk access */
#define QCOW2_OL_CACHED \

View File

@ -177,7 +177,7 @@ static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset,
/* There's not enough space for the write, or the read request is
* out-of-range. Don't read/write anything to prevent leaking out of
* the size specified in options. */
return is_write ? -ENOSPC : -EINVAL;;
return is_write ? -ENOSPC : -EINVAL;
}
if (*offset > INT64_MAX - s->offset) {

View File

@ -333,6 +333,12 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
if (!s->cid_checked && bs->backing) {
BlockDriverState *p_bs = bs->backing->bs;
if (strcmp(p_bs->drv->format_name, "vmdk")) {
/* Backing file is not in vmdk format, so it does not have
* a CID, which makes the overlay's parent CID invalid */
return 0;
}
if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) {
/* read failure: report as not valid */
return 0;

View File

@ -2696,18 +2696,21 @@
# @template: Specifies a template mode which can be adjusted using the other
# flags, defaults to 'cached'
#
# @bitmap-directory: since 3.0
#
# Since: 2.9
##
{ 'struct': 'Qcow2OverlapCheckFlags',
'data': { '*template': 'Qcow2OverlapCheckMode',
'*main-header': 'bool',
'*active-l1': 'bool',
'*active-l2': 'bool',
'*refcount-table': 'bool',
'*refcount-block': 'bool',
'*snapshot-table': 'bool',
'*inactive-l1': 'bool',
'*inactive-l2': 'bool' } }
'data': { '*template': 'Qcow2OverlapCheckMode',
'*main-header': 'bool',
'*active-l1': 'bool',
'*active-l2': 'bool',
'*refcount-table': 'bool',
'*refcount-block': 'bool',
'*snapshot-table': 'bool',
'*inactive-l1': 'bool',
'*inactive-l2': 'bool',
'*bitmap-directory': 'bool' } }
##
# @Qcow2OverlapChecks:

132
tests/qemu-iotests/225 Executable file
View File

@ -0,0 +1,132 @@
#!/bin/bash
#
# Test vmdk backing file correlation
#
# Copyright (C) 2018 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=mreitz@redhat.com
seq=$(basename $0)
echo "QA output created by $seq"
here=$PWD
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
rm -f "$TEST_IMG.not_base"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
. ./common.qemu
# This tests vmdk-specific low-level functionality
_supported_fmt vmdk
_supported_proto file
_supported_os Linux
_unsupported_imgopts "subformat=monolithicFlat" \
"subformat=twoGbMaxExtentFlat" \
"subformat=twoGbMaxExtentSparse"
TEST_IMG="$TEST_IMG.base" _make_test_img 1M
TEST_IMG="$TEST_IMG.not_base" _make_test_img 1M
_make_test_img -b "$TEST_IMG.base"
make_opts()
{
node_name=$1
filename=$2
backing=$3
if [ -z "$backing" ]; then
backing="null"
else
backing="'$backing'"
fi
echo "{ 'node-name': '$node_name',
'driver': 'vmdk',
'file': {
'driver': 'file',
'filename': '$filename'
},
'backing': $backing }"
}
overlay_opts=$(make_opts overlay "$TEST_IMG" backing)
base_opts=$(make_opts backing "$TEST_IMG.base")
not_base_opts=$(make_opts backing "$TEST_IMG.not_base")
not_vmdk_opts="{ 'node-name': 'backing', 'driver': 'null-co' }"
echo
echo '=== Testing fitting VMDK backing image ==='
echo
qemu_comm_method=monitor \
_launch_qemu -blockdev "$base_opts" -blockdev "$overlay_opts"
# Should not return an error
_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'ops'
_cleanup_qemu
echo
echo '=== Testing unrelated VMDK backing image ==='
echo
qemu_comm_method=monitor \
_launch_qemu -blockdev "$not_base_opts" -blockdev "$overlay_opts"
# Should fail (gracefully)
_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'failed'
_cleanup_qemu
echo
echo '=== Testing non-VMDK backing image ==='
echo
# FIXME: This is the reason why we have to use two -blockdev
# invocations. You can only fully override the backing file options
# if you either specify a node reference (as done here) or the new
# options contain file.filename (which in this case they do not).
# In other cases, file.filename will be set to whatever the image
# header of the overlay contains (which we do not want). I consider
# this a FIXME because with -blockdev, you cannot specify "partial"
# options, so setting file.filename but leaving the rest as specified
# by the user does not make sense.
qemu_comm_method=monitor \
_launch_qemu -blockdev "$not_vmdk_opts" -blockdev "$overlay_opts"
# Should fail (gracefully)
_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'failed'
_cleanup_qemu
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View File

@ -0,0 +1,24 @@
QA output created by 225
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/t.IMGFMT.not_base', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
=== Testing fitting VMDK backing image ===
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qemu-io overlay "read 0 512"
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing unrelated VMDK backing image ===
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qemu-io overlay "read 0 512"
read failed: Invalid argument
=== Testing non-VMDK backing image ===
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qemu-io overlay "read 0 512"
read failed: Invalid argument
*** done

View File

@ -222,3 +222,4 @@
221 rw auto quick
222 rw auto quick
223 rw auto quick
225 rw auto quick