qcow2: Don't crash qemu-img info with missing crypto header

qcow2_refresh_limits() assumes that s->crypto is non-NULL whenever
bs->encrypted is true. This is actually not the case: qcow2_do_open()
allows to open an image with a missing crypto header for BDRV_O_NO_IO,
and then bs->encrypted is true, but s->crypto is still NULL.

It doesn't make sense to open an invalid image, so remove the exception
for BDRV_O_NO_IO. This catches the problem early and any code that makes
the same assumption is safe now.

At the same time, in the name of defensive programming, we shouldn't
make the assumption in the first place. Let qcow2_refresh_limits() check
s->crypto rather than bs->encrypted. If s->crypto is NULL, it also can't
make any requirement on request alignment.

Finally, start a qcow2-encryption test case that only serves as a
regression test for this crash for now.

Reported-by: Leonid Reviakin <L.reviakin@fobos-nt.ru>
Reported-by: Denis Rastyogin <gerben@altlinux.org>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20250318201143.70657-1-kwolf@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Kevin Wolf 2025-03-18 21:11:43 +01:00
parent 6b36a57831
commit 8e4ffb4ef4
3 changed files with 109 additions and 2 deletions

View File

@ -1721,7 +1721,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL;
goto fail;
}
} else if (!(flags & BDRV_O_NO_IO)) {
} else {
error_setg(errp, "Missing CRYPTO header for crypt method %d",
s->crypt_method_header);
ret = -EINVAL;
@ -1976,7 +1976,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
if (bs->encrypted) {
if (s->crypto) {
/* Encryption works on a sector granularity */
bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto);
}

View File

@ -0,0 +1,75 @@
#!/usr/bin/env bash
# group: rw quick
#
# Test case for encryption support in qcow2
#
# Copyright (C) 2025 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=kwolf@redhat.com
seq="$(basename $0)"
echo "QA output created by $seq"
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ../common.rc
. ../common.filter
# This tests qcow2-specific low-level functionality
_supported_fmt qcow2
_supported_proto file
_require_working_luks
IMG_SIZE=64M
echo
echo "=== Create an encrypted image ==="
echo
_make_test_img --object secret,id=sec0,data=123456 -o encrypt.format=luks,encrypt.key-secret=sec0 $IMG_SIZE
$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts
_img_info
$QEMU_IMG check \
--object secret,id=sec0,data=123456 \
--image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 \
| _filter_qemu_img_check
echo
echo "=== Remove the header extension ==="
echo
$PYTHON ../qcow2.py "$TEST_IMG" del-header-ext 0x0537be77
$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts
_img_info
$QEMU_IMG check \
--object secret,id=sec0,data=123456 \
--image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 2>&1 \
| _filter_qemu_img_check \
| _filter_testdir
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View File

@ -0,0 +1,32 @@
QA output created by qcow2-encryption
=== Create an encrypted image ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Header extension:
magic 0x537be77 (Crypto header)
length 16
data <binary>
Header extension:
magic 0x6803f857 (Feature table)
length 384
data <binary>
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64 MiB (67108864 bytes)
encrypted: yes
cluster_size: 65536
No errors were found on the image.
=== Remove the header extension ===
Header extension:
magic 0x6803f857 (Feature table)
length 384
data <binary>
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Missing CRYPTO header for crypt method 2
qemu-img: Could not open 'file.filename=TEST_DIR/t.qcow2,encrypt.key-secret=sec0': Missing CRYPTO header for crypt method 2
*** done