From b979eb91ef5be02b696849935df9c81333c52d58 Mon Sep 17 00:00:00 2001 From: Miroslav Rezanina Date: Mon, 18 Aug 2025 01:21:13 -0400 Subject: [PATCH] * Mon Aug 18 2025 Miroslav Rezanina - 10.0.0-11 - kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch [RHEL-105440] - Resolves: RHEL-105440 (Openstack guest becomes inaccessible via network when storage network on the hypervisor is disabled/lost [rhel-10.1]) --- ...drv_get_specific_info-implementation.patch | 273 ++++++++++++++++++ qemu-kvm.spec | 9 +- 2 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch diff --git a/kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch b/kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch new file mode 100644 index 0000000..67f27e7 --- /dev/null +++ b/kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch @@ -0,0 +1,273 @@ +From 1fe4e3379f5f3ae4e5554b18b4b8c50fedd9203f Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Mon, 11 Aug 2025 15:40:10 +0200 +Subject: [PATCH] rbd: Fix .bdrv_get_specific_info implementation + +RH-Author: Kevin Wolf +RH-MergeRequest: 399: rbd: Fix .bdrv_get_specific_info implementation +RH-Jira: RHEL-105440 +RH-Acked-by: Hanna Czenczek +RH-Acked-by: Stefan Hajnoczi +RH-Commit: [1/1] 840c954c84c97f768ae6cfb4aa0e2766b22a6f06 (kmwolf/centos-qemu-kvm) + +qemu_rbd_get_specific_info() has at least two problems: + +The first is that it issues a blocking rbd_read() call in order to probe +the encryption format for the image while querying the node. This means +that if the connection to the server goes down, not only I/O is stuck +(which is unavoidable), but query-names-block-nodes will actually make +the whole QEMU instance unresponsive. .bdrv_get_specific_info +implementations shouldn't perform blocking operations, but only return +what is already known. + +The second is that the information returned isn't even correct. If the +image is already opened with encryption enabled at the RBD level, we'll +probe for "double encryption", i.e. if the encrypted data contains +another encryption header. If it doesn't (which is the normal case), we +won't return the encryption format. If it does, we return misleading +information because it looks like we're talking about the outer level +(the encryption format of the image itself) while the information is +about an encryption header in the guest data. + +Fix this by storing the encryption format in BDRVRBDState when the image +is opened (and we do blocking operations anyway) and returning only the +stored information in qemu_rbd_get_specific_info(). + +The information we'll store is either the actual encryption format that +we enabled on the RBD level, or if the image is unencrypted, the result +of the same probing as we previously did when querying the node. Probing +image formats based on content that can be modified by the guest has +long been known as problematic, but as long as we only output it to the +user instead of making decisions based on it, it should be okay. It is +undoubtedly useful in the context of 'qemu-img info' when you're trying +to figure out which encryption options you have to use to open the +image successfully. + +Fixes: 42e4ac9ef5a6 ("block/rbd: Add support for rbd image encryption") +Buglink: https://issues.redhat.com/browse/RHEL-105440 +Signed-off-by: Kevin Wolf +Message-ID: <20250811134010.81787-1-kwolf@redhat.com> +Reviewed-by: Hanna Czenczek +Signed-off-by: Kevin Wolf +(cherry picked from commit 4af976ef398e4e823addc00bf1c58787ba4952fe) +Signed-off-by: Kevin Wolf +--- + block/rbd.c | 104 ++++++++++++++++++++++++++++--------------- + qapi/block-core.json | 9 +++- + 2 files changed, 76 insertions(+), 37 deletions(-) + +diff --git a/block/rbd.c b/block/rbd.c +index 4f3d42a8e7..9b7b834f04 100644 +--- a/block/rbd.c ++++ b/block/rbd.c +@@ -99,6 +99,14 @@ typedef struct BDRVRBDState { + char *namespace; + uint64_t image_size; + uint64_t object_size; ++ ++ /* ++ * If @bs->encrypted is true, this is the encryption format actually loaded ++ * at the librbd level. If it is false, it is the result of probing. ++ * RBD_IMAGE_ENCRYPTION_FORMAT__MAX means that encryption is not enabled and ++ * probing didn't find any known encryption header either. ++ */ ++ RbdImageEncryptionFormat encryption_format; + } BDRVRBDState; + + typedef struct RBDTask { +@@ -471,10 +479,12 @@ static int qemu_rbd_encryption_format(rbd_image_t image, + return 0; + } + +-static int qemu_rbd_encryption_load(rbd_image_t image, ++static int qemu_rbd_encryption_load(BlockDriverState *bs, ++ rbd_image_t image, + RbdEncryptionOptions *encrypt, + Error **errp) + { ++ BDRVRBDState *s = bs->opaque; + int r = 0; + g_autofree char *passphrase = NULL; + rbd_encryption_luks1_format_options_t luks_opts; +@@ -545,15 +555,19 @@ static int qemu_rbd_encryption_load(rbd_image_t image, + error_setg_errno(errp, -r, "encryption load fail"); + return r; + } ++ bs->encrypted = true; ++ s->encryption_format = encrypt->format; + + return 0; + } + + #ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 +-static int qemu_rbd_encryption_load2(rbd_image_t image, ++static int qemu_rbd_encryption_load2(BlockDriverState *bs, ++ rbd_image_t image, + RbdEncryptionOptions *encrypt, + Error **errp) + { ++ BDRVRBDState *s = bs->opaque; + int r = 0; + int encrypt_count = 1; + int i; +@@ -639,6 +653,8 @@ static int qemu_rbd_encryption_load2(rbd_image_t image, + error_setg_errno(errp, -r, "layered encryption load fail"); + goto exit; + } ++ bs->encrypted = true; ++ s->encryption_format = encrypt->format; + + exit: + for (i = 0; i < encrypt_count; ++i) { +@@ -672,6 +688,45 @@ exit: + #endif + #endif + ++/* ++ * For an image without encryption enabled on the rbd layer, probe the start of ++ * the image if it could be opened as an encrypted image so that we can display ++ * it when the user queries the node (most importantly in qemu-img). ++ * ++ * If the guest writes an encryption header to its disk after this probing, this ++ * won't be reflected when queried, but that's okay. There is no reason why the ++ * user should want to apply encryption at the rbd level while the image is ++ * still in use. This is just guest data. ++ */ ++static void qemu_rbd_encryption_probe(BlockDriverState *bs) ++{ ++ BDRVRBDState *s = bs->opaque; ++ char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; ++ int r; ++ ++ assert(s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX); ++ ++ r = rbd_read(s->image, 0, ++ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); ++ if (r < RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { ++ return; ++ } ++ ++ if (memcmp(buf, rbd_luks_header_verification, ++ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { ++ s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; ++ } else if (memcmp(buf, rbd_luks2_header_verification, ++ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { ++ s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; ++ } else if (memcmp(buf, rbd_layered_luks_header_verification, ++ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { ++ s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; ++ } else if (memcmp(buf, rbd_layered_luks2_header_verification, ++ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { ++ s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; ++ } ++} ++ + /* FIXME Deprecate and remove keypairs or make it available in QMP. */ + static int qemu_rbd_do_create(BlockdevCreateOptions *options, + const char *keypairs, const char *password_secret, +@@ -1134,17 +1189,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + goto failed_open; + } + ++ s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT__MAX; + if (opts->encrypt) { + #ifdef LIBRBD_SUPPORTS_ENCRYPTION + if (opts->encrypt->parent) { + #ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 +- r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp); ++ r = qemu_rbd_encryption_load2(bs, s->image, opts->encrypt, errp); + #else + r = -ENOTSUP; + error_setg(errp, "RBD library does not support layered encryption"); + #endif + } else { +- r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp); ++ r = qemu_rbd_encryption_load(bs, s->image, opts->encrypt, errp); + } + if (r < 0) { + goto failed_post_open; +@@ -1154,6 +1210,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + error_setg(errp, "RBD library does not support image encryption"); + goto failed_post_open; + #endif ++ } else { ++ qemu_rbd_encryption_probe(bs); + } + + r = rbd_stat(s->image, &info, sizeof(info)); +@@ -1413,17 +1471,6 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, + { + BDRVRBDState *s = bs->opaque; + ImageInfoSpecific *spec_info; +- char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; +- int r; +- +- if (s->image_size >= RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { +- r = rbd_read(s->image, 0, +- RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); +- if (r < 0) { +- error_setg_errno(errp, -r, "cannot read image start for probe"); +- return NULL; +- } +- } + + spec_info = g_new(ImageInfoSpecific, 1); + *spec_info = (ImageInfoSpecific){ +@@ -1431,28 +1478,13 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, + .u.rbd.data = g_new0(ImageInfoSpecificRbd, 1), + }; + +- if (memcmp(buf, rbd_luks_header_verification, +- RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { +- spec_info->u.rbd.data->encryption_format = +- RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; +- spec_info->u.rbd.data->has_encryption_format = true; +- } else if (memcmp(buf, rbd_luks2_header_verification, +- RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { +- spec_info->u.rbd.data->encryption_format = +- RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; +- spec_info->u.rbd.data->has_encryption_format = true; +- } else if (memcmp(buf, rbd_layered_luks_header_verification, +- RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { +- spec_info->u.rbd.data->encryption_format = +- RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; +- spec_info->u.rbd.data->has_encryption_format = true; +- } else if (memcmp(buf, rbd_layered_luks2_header_verification, +- RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { +- spec_info->u.rbd.data->encryption_format = +- RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; +- spec_info->u.rbd.data->has_encryption_format = true; ++ if (s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX) { ++ assert(!bs->encrypted); + } else { +- spec_info->u.rbd.data->has_encryption_format = false; ++ ImageInfoSpecificRbd *rbd_info = spec_info->u.rbd.data; ++ ++ rbd_info->has_encryption_format = true; ++ rbd_info->encryption_format = s->encryption_format; + } + + return spec_info; +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 7f70ec6d3c..d00161af87 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -158,7 +158,14 @@ + ## + # @ImageInfoSpecificRbd: + # +-# @encryption-format: Image encryption format ++# @encryption-format: Image encryption format. If encryption is enabled for the ++# image (see encrypted in BlockNodeInfo), this is the actual format in which the ++# image is accessed. If encryption is not enabled, this is the result of ++# probing when the image was opened, to give a suggestion which encryption ++# format could be enabled. Note that probing results can be changed by the ++# guest by writing a (possibly partial) encryption format header to the ++# image, so don't treat this information as trusted if the guest is not ++# trusted. + # + # Since: 6.1 + ## +-- +2.39.3 + diff --git a/qemu-kvm.spec b/qemu-kvm.spec index 9b820ce..0cebb78 100644 --- a/qemu-kvm.spec +++ b/qemu-kvm.spec @@ -143,7 +143,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \ Summary: QEMU is a machine emulator and virtualizer Name: qemu-kvm Version: 10.0.0 -Release: 10%{?rcrel}%{?dist}%{?cc_suffix} +Release: 11%{?rcrel}%{?dist}%{?cc_suffix} # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped # Epoch 15 used for RHEL 8 # Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5) @@ -418,6 +418,8 @@ Patch127: kvm-iotests-graph-changes-while-io-add-test-case-with-re.patch Patch128: kvm-Declare-rtl8139-as-deprecated.patch # For RHEL-102325 - [qemu] enable variable service for edk2 Patch129: kvm-Enable-uefi-variable-service-for-edk2.patch +# For RHEL-105440 - Openstack guest becomes inaccessible via network when storage network on the hypervisor is disabled/lost [rhel-10.1] +Patch130: kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch %if %{have_clang} BuildRequires: clang @@ -1500,6 +1502,11 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %endif %changelog +* Mon Aug 18 2025 Miroslav Rezanina - 10.0.0-11 +- kvm-rbd-Fix-.bdrv_get_specific_info-implementation.patch [RHEL-105440] +- Resolves: RHEL-105440 + (Openstack guest becomes inaccessible via network when storage network on the hypervisor is disabled/lost [rhel-10.1]) + * Tue Aug 12 2025 Miroslav Rezanina - 10.0.0-10 - kvm-Enable-uefi-variable-service-for-edk2.patch [RHEL-102325] - Resolves: RHEL-102325