libvirt-11.10.0-2.el9

- qemu: tpm: Account for possible migration without actually sharing storage (RHEL-108915)
- tests: Test virFileIsSharedFSOverride (RHEL-135287)
- util: Fix race condition in virFileIsSharedFSType (RHEL-135287)
- util: Fix race condition in virFileIsSharedFSOverride (RHEL-135287)
- util: Rework virFileIsSharedFSOverride using virFileCheckParents (RHEL-135287)

Resolves: RHEL-108915, RHEL-135287
This commit is contained in:
Jiri Denemark 2025-12-18 16:04:16 +01:00
parent 14c96f4934
commit a3cafd28ee
6 changed files with 589 additions and 1 deletions

View File

@ -0,0 +1,119 @@
From 582ac1d5b308d1b9816c57ebca762a8796c67df4 Mon Sep 17 00:00:00 2001
Message-ID: <582ac1d5b308d1b9816c57ebca762a8796c67df4.1766070256.git.jdenemar@redhat.com>
From: Peter Krempa <pkrempa@redhat.com>
Date: Mon, 1 Dec 2025 11:35:32 +0100
Subject: [PATCH] qemu: tpm: Account for possible migration without actually
sharing storage
The current logic in 'qemuTPMEmulatorBuildCommand' skips all setup if
the *location* of the data is on what we'd consider shared storage.
This means that if the location is not actually shared (e.g. it's shared
betweeh some other hosts than the two doing the migration) and the path
wasn't ever used (e.g. by migrating out) from the host where we're
migrating into the complete setup of the location would be skipped even
when it doesn't exist.
Fix the logic by skipping only some of the setup steps so that
'qemuTPMEmulatorCreateStorage' can still create the storage if it
doesn't exist.
The rest of the code then needs to take the 'created' flag returned from
'qemuTPMEmulatorCreateStorage' into account.
Fixes: 68103e9daf633b789428fedef56f816c92f6ee75
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit d56d0560946770d4364a4918cc289e6a7fe5d15c)
https://issues.redhat.com/browse/RHEL-108915
---
src/qemu/qemu_tpm.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
index 4c9445d72c..660410bcba 100644
--- a/src/qemu/qemu_tpm.c
+++ b/src/qemu/qemu_tpm.c
@@ -158,6 +158,7 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
/**
* qemuTPMEmulatorCreateStorage:
* @tpm: TPM definition for an emulator type
+ * @sharedStorageMigration: VM is being migrated with possibly shared storage
* @created: a pointer to a bool that will be set to true if the
* storage was created because it did not exist yet
* @swtpm_user: The uid that needs to be able to access the directory
@@ -169,6 +170,7 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
*/
static int
qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm,
+ bool sharedStorageMigration,
bool *created,
uid_t swtpm_user,
gid_t swtpm_group)
@@ -187,8 +189,17 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm,
*created = false;
if (!virFileExists(source_path) ||
- virDirIsEmpty(source_path, true) > 0)
+ virDirIsEmpty(source_path, true) > 0) {
*created = true;
+ } else {
+ /* If the location exists and is shared, we don't need to create it
+ * during migration */
+ if (sharedStorageMigration) {
+ VIR_DEBUG("Skipping TPM storage creation. Path '%s' already exists and is on shared storage.",
+ source_path);
+ return 0;
+ }
+ }
if (virDirCreate(source_path, 0700, swtpm_user, swtpm_group,
VIR_DIR_CREATE_ALLOW_EXIST) < 0) {
@@ -809,16 +820,13 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
run_setup = true;
}
- /* Do not create storage and run swtpm_setup on incoming migration over
- * shared storage
- */
on_shared_storage = virFileIsSharedFS(tpm->data.emulator.source_path,
cfg->sharedFilesystems) == 1;
- if (incomingMigration && on_shared_storage)
- create_storage = false;
if (create_storage) {
- if (qemuTPMEmulatorCreateStorage(tpm, &created,
+ if (qemuTPMEmulatorCreateStorage(tpm,
+ incomingMigration && on_shared_storage,
+ &created,
cfg->swtpm_user, cfg->swtpm_group) < 0)
return NULL;
run_setup = created;
@@ -885,6 +893,9 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
/* If swtpm supports it and the TPM state is stored on shared storage,
* start swtpm with --migration release-lock-outgoing so it can migrate
* across shared storage if needed.
+ *
+ * Note that if 'created' is true, the location didn't exist so the storage
+ * is not actually shared.
*/
QEMU_DOMAIN_TPM_PRIVATE(tpm)->swtpm.can_migrate_shared_storage = false;
if (on_shared_storage &&
@@ -892,13 +903,13 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
virCommandAddArg(cmd, "--migration");
virCommandAddArgFormat(cmd, "release-lock-outgoing%s",
- incomingMigration ? ",incoming": "");
+ incomingMigration && !created ? ",incoming": "");
QEMU_DOMAIN_TPM_PRIVATE(tpm)->swtpm.can_migrate_shared_storage = true;
} else {
/* Report an error if there's an incoming migration across shared
* storage and swtpm does not support the --migration option.
*/
- if (incomingMigration && on_shared_storage) {
+ if (incomingMigration && on_shared_storage && !created) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
_("%1$s (on destination side) does not support the --migration option needed for migration with shared storage"),
swtpm);
--
2.52.0

View File

@ -0,0 +1,111 @@
From b630429647207ba0d406cf855e590ddaa98fdb9b Mon Sep 17 00:00:00 2001
Message-ID: <b630429647207ba0d406cf855e590ddaa98fdb9b.1766070256.git.jdenemar@redhat.com>
From: Jiri Denemark <jdenemar@redhat.com>
Date: Fri, 5 Dec 2025 15:09:15 +0100
Subject: [PATCH] tests: Test virFileIsSharedFSOverride
Technically virFileIsSharedFSOverride is available on any OS, but we
need a mocked realpath() to test it. Because the virfilemock library
also mocks statfs() which is only available on Linux, we don't even try
to load the library anywhere else. Thus we need to skip testing
virFileIsSharedFSOverride on non-Linux too.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
(cherry picked from commit 121d179e068b584f62ea2c029d89a44e67c909c0)
https://issues.redhat.com/browse/RHEL-135287
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
tests/virfiletest.c | 69 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/tests/virfiletest.c b/tests/virfiletest.c
index e05925a321..ccd76a3fac 100644
--- a/tests/virfiletest.c
+++ b/tests/virfiletest.c
@@ -329,6 +329,55 @@ testFileIsSharedFSType(const void *opaque G_GNUC_UNUSED)
}
+static const char *shared_filesystems[] = {
+ "/run/user/501/gvfs",
+ "/nfs",
+ "/gluster",
+ "/ceph/multi",
+ "/gpfs/data/blaf",
+ "/quobyte",
+ NULL,
+};
+
+static int
+testFileIsSharedFSOverride(const void *opaque G_GNUC_UNUSED)
+{
+#ifndef __linux__
+ return EXIT_AM_SKIP;
+#else
+ const struct testFileIsSharedFSType *data = opaque;
+ g_autofree char *mtabFile = NULL;
+ bool actual;
+ int ret = -1;
+
+ /* mtab is used by mocked realpath to decide whether a given path exists */
+ mtabFile = g_strdup_printf(abs_srcdir "/virfiledata/%s", data->mtabFile);
+
+ if (!g_setenv("LIBVIRT_MTAB", mtabFile, true)) {
+ fprintf(stderr, "Unable to set env variable\n");
+ goto cleanup;
+ }
+
+ actual = virFileIsSharedFSOverride(data->filename,
+ (char * const *) shared_filesystems);
+
+ if (actual != data->expected) {
+ fprintf(stderr, "FS of '%s' is %s. Expected: %s\n",
+ data->filename,
+ actual ? "shared" : "not shared",
+ data->expected ? "shared" : "not shared");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ g_unsetenv("LIBVIRT_MTAB");
+ return ret;
+#endif
+}
+
+
static int
mymain(void)
{
@@ -439,6 +488,26 @@ mymain(void)
DO_TEST_FILE_IS_SHARED_FS_TYPE("mounts3.txt", "/gpfs/data", true);
DO_TEST_FILE_IS_SHARED_FS_TYPE("mounts3.txt", "/quobyte", true);
+#define DO_TEST_FILE_IS_SHARED_FS_OVERRIDE(mtab, file, exp) \
+ do { \
+ struct testFileIsSharedFSType data = { \
+ .mtabFile = mtab, .filename = file, .expected = exp \
+ }; \
+ if (virTestRun(virTestCounterNext(), testFileIsSharedFSOverride, &data) < 0) \
+ ret = -1; \
+ } while (0)
+
+ virTestCounterReset("testFileIsSharedFSOverride ");
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts2.txt", "/boot/vmlinuz", false);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts2.txt", "/run/user/501/gvfs/some/file", true);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/nfs/file", true);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/gluster/file", true);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/some/symlink/file", true);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/ceph/file", false);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/ceph/multi/file", true);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/gpfs/data", false);
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/quobyte", true);
+
return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
--
2.52.0

View File

@ -0,0 +1,114 @@
From 7f7d60e42f39deaec69318b93cf922f1dda54a26 Mon Sep 17 00:00:00 2001
Message-ID: <7f7d60e42f39deaec69318b93cf922f1dda54a26.1766070256.git.jdenemar@redhat.com>
From: Jiri Denemark <jdenemar@redhat.com>
Date: Fri, 5 Dec 2025 16:51:25 +0100
Subject: [PATCH] util: Fix race condition in virFileIsSharedFSOverride
Switch virFileIsSharedFSOverride to use virFileCheckParents to avoid a
race which could result in virFileCanonicalizePath to be called on a
path that does not exist anymore.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
(cherry picked from commit 3a44f0c23d75519a9a374f790f4b91ab7b65a138)
https://issues.redhat.com/browse/RHEL-135287
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
src/util/virfile.c | 59 +++++++++++++++++-----------------------------
1 file changed, 22 insertions(+), 37 deletions(-)
diff --git a/src/util/virfile.c b/src/util/virfile.c
index 95fc8ff0e6..52d711d2a9 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -3502,33 +3502,6 @@ virFileCheckParents(const char *path,
}
-static char *
-virFileGetExistingParent(const char *path)
-{
- g_autofree char *dirpath = g_strdup(path);
- char *p = NULL;
-
- /* Try less and less of the path until we get to a directory we can access.
- * Even if we don't have 'x' permission on any directory in the path on the
- * NFS server (assuming it's NFS), we will be able to stat the mount point.
- */
- while (!virFileExists(dirpath) && p != dirpath) {
- if (!(p = strrchr(dirpath, '/'))) {
- virReportSystemError(EINVAL,
- _("Invalid relative path '%1$s'"), path);
- return NULL;
- }
-
- if (p == dirpath)
- *(p + 1) = '\0';
- else
- *p = '\0';
- }
-
- return g_steal_pointer(&dirpath);
-}
-
-
#ifdef __linux__
# ifndef NFS_SUPER_MAGIC
@@ -3875,6 +3848,17 @@ virFileGetDefaultHugepage(virHugeTLBFS *fs,
}
+static bool
+virFileCheckParentsCanonicalize(const char *path,
+ void *opaque)
+{
+ char **canonical = opaque;
+
+ *canonical = virFileCanonicalizePath(path);
+ return !!*canonical;
+}
+
+
/**
* virFileIsSharedFSOverride:
* @path: Path to check
@@ -3888,24 +3872,25 @@ virFileIsSharedFSOverride(const char *path,
char *const *overrides)
{
g_autofree char *dirpath = NULL;
- g_autofree char *existing = NULL;
char *p = NULL;
+ int rc;
if (!path || path[0] != '/' || !overrides)
return false;
/* We only care about the longest existing sub-path. Further components
- * may will later be created by libvirt will not magically become a shared
- * filesystem. */
- if (!(existing = virFileGetExistingParent(path)))
+ * that may later be created by libvirt will not magically become a shared
+ * filesystem. Overrides have been canonicalized ahead of time, so we need
+ * to do the same for the provided path or we'll never be able to find a
+ * match if symlinks are involved.
+ */
+ rc = virFileCheckParents(path, NULL,
+ virFileCheckParentsCanonicalize, &dirpath);
+ if (rc == -1)
return false;
- /* Overrides have been canonicalized ahead of time, so we need to
- * do the same for the provided path or we'll never be able to
- * find a match if symlinks are involved */
- if (!(dirpath = virFileCanonicalizePath(existing))) {
- VIR_DEBUG("Cannot canonicalize parent '%s' of path '%s'",
- existing, path);
+ if (rc != 0) {
+ VIR_DEBUG("Cannot canonicalize path '%s'", path);
return false;
}
--
2.52.0

View File

@ -0,0 +1,144 @@
From 4596ee4c2fe33d3b44a44b120d61052ac943bae4 Mon Sep 17 00:00:00 2001
Message-ID: <4596ee4c2fe33d3b44a44b120d61052ac943bae4.1766070256.git.jdenemar@redhat.com>
From: Jiri Denemark <jdenemar@redhat.com>
Date: Fri, 5 Dec 2025 16:47:14 +0100
Subject: [PATCH] util: Fix race condition in virFileIsSharedFSType
virFileIsSharedFSType could end up calling statfs on a path that no
longer exists and return an error. If this happens for a path on a
shared filesystem, the caller may incorrectly consider the path as
non-shared.
Specifically, when starting a domain with TPM enabled and deciding
whether its vTPM state is stored on a shared storage, the race could
cause qemuTPMEmulatorBuildCommand to consider the state to be
non-shared. This means swtpm would be started without --migration even
when the state is actually stored on a shared storage and any attempt to
migrate such domain would fail with
Operation not supported: the running swtpm does not support
migration with shared storage
In fact, any caller of virFileGetExistingParent contained an inherent
TOCTOU race condition as the existing parent of a given path return by
virFileGetExistingParent may no longer exist at the time the caller
wants to check it.
This patch introduces a new virFileCheckParents API which is almost
identical to virFileGetExistingParent, but uses a supplied callback to
check each path. This new API is used in virFileIsSharedFSType to avoid
the race. The old function will later be completely removed once all
callers are switched to the new one.
Fixes: 05526b50909ff50c16e13a0b5580d41de74e3d59
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
(cherry picked from commit b6addd42bece693debbf2e95551a2b4d2e1b453f)
https://issues.redhat.com/browse/RHEL-135287
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
src/util/virfile.c | 71 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 69 insertions(+), 2 deletions(-)
diff --git a/src/util/virfile.c b/src/util/virfile.c
index a5c9fbe0d9..95fc8ff0e6 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -3445,6 +3445,63 @@ virFileRemoveLastComponent(char *path)
}
+/* Check callback for virFileCheckParents */
+typedef bool (*virFileCheckParentsCallback)(const char *dirpath,
+ void *opaque);
+
+/**
+ * virFileCheckParents:
+ * @path: path to check
+ * @parent: where to store the closest parent satisfying the check
+ * @check: callback called on parent paths
+ * @opaque: data for the @check callback
+ *
+ * Calls @check on the @path and its parent paths until it returns true or a
+ * root directory is reached. When @check returns true, the @parent (if
+ * non-NULL) will be set to a copy of the corresponding path. The caller is
+ * responsible for freeing it.
+ *
+ * Returns 0 on success (@parent set),
+ * -1 on invalid input,
+ * -2 when no path (including "/") satisfies the @check.
+ */
+static int
+virFileCheckParents(const char *path,
+ char **parent,
+ virFileCheckParentsCallback check,
+ void *opaque)
+{
+ g_autofree char *dirpath = g_strdup(path);
+ char *p = NULL;
+ bool checkOK;
+
+ checkOK = check(dirpath, opaque);
+
+ while (!checkOK && p != dirpath) {
+ if (!(p = strrchr(dirpath, G_DIR_SEPARATOR))) {
+ virReportSystemError(EINVAL,
+ _("Invalid absolute path '%1$s'"), path);
+ return -1;
+ }
+
+ if (p == dirpath)
+ *(p + 1) = '\0';
+ else
+ *p = '\0';
+
+ checkOK = check(dirpath, opaque);
+ }
+
+ if (!checkOK)
+ return -2;
+
+ if (parent)
+ *parent = g_steal_pointer(&dirpath);
+
+ return 0;
+}
+
+
static char *
virFileGetExistingParent(const char *path)
{
@@ -3599,6 +3656,14 @@ static const struct virFileSharedFsData virFileSharedFs[] = {
};
+static bool
+virFileCheckParentsStatFS(const char *path,
+ void *opaque)
+{
+ return statfs(path, (struct statfs *) opaque) == 0;
+}
+
+
int
virFileIsSharedFSType(const char *path,
unsigned int fstypes)
@@ -3607,11 +3672,13 @@ virFileIsSharedFSType(const char *path,
struct statfs sb;
long long f_type = 0;
size_t i;
+ int rc;
- if (!(dirpath = virFileGetExistingParent(path)))
+ if ((rc = virFileCheckParents(path, &dirpath,
+ virFileCheckParentsStatFS, &sb)) == -1)
return -1;
- if (statfs(dirpath, &sb) < 0) {
+ if (rc != 0) {
virReportSystemError(errno,
_("cannot determine filesystem for '%1$s'"),
path);
--
2.52.0

View File

@ -0,0 +1,84 @@
From d85627338e531618aa72b6039483b0d0a3e3d474 Mon Sep 17 00:00:00 2001
Message-ID: <d85627338e531618aa72b6039483b0d0a3e3d474.1766070256.git.jdenemar@redhat.com>
From: Jiri Denemark <jdenemar@redhat.com>
Date: Fri, 5 Dec 2025 16:52:32 +0100
Subject: [PATCH] util: Rework virFileIsSharedFSOverride using
virFileCheckParents
The newly introduced virFileCheckParents is generic enough to be used
for checking whether a specific path or any of its parents is included
in the overrides array.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
(cherry picked from commit eedf9ed68b45585569865604bf2a403670feaf3e)
https://issues.redhat.com/browse/RHEL-135287
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
src/util/virfile.c | 35 ++++++++++++-----------------------
1 file changed, 12 insertions(+), 23 deletions(-)
diff --git a/src/util/virfile.c b/src/util/virfile.c
index 52d711d2a9..05b2fa8168 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -3859,6 +3859,14 @@ virFileCheckParentsCanonicalize(const char *path,
}
+static bool
+virFileCheckParentsInOverrides(const char *path,
+ void *opaque)
+{
+ return g_strv_contains((const char *const *) opaque, path);
+}
+
+
/**
* virFileIsSharedFSOverride:
* @path: Path to check
@@ -3872,7 +3880,6 @@ virFileIsSharedFSOverride(const char *path,
char *const *overrides)
{
g_autofree char *dirpath = NULL;
- char *p = NULL;
int rc;
if (!path || path[0] != '/' || !overrides)
@@ -3894,29 +3901,11 @@ virFileIsSharedFSOverride(const char *path,
return false;
}
- if (g_strv_contains((const char *const *) overrides, dirpath))
- return true;
+ if (virFileCheckParents(dirpath, NULL, virFileCheckParentsInOverrides,
+ (void *) overrides) < 0)
+ return false;
- /* Continue until we've scanned the entire path */
- while (p != dirpath) {
-
- /* Find the last slash */
- if ((p = strrchr(dirpath, '/')) == NULL)
- break;
-
- /* Truncate the path by overwriting the slash that we've just
- * found with a null byte. If it is the very first slash in
- * the path, we need to handle things slightly differently */
- if (p == dirpath)
- *(p+1) = '\0';
- else
- *p = '\0';
-
- if (g_strv_contains((const char *const *) overrides, dirpath))
- return true;
- }
-
- return false;
+ return true;
}
--
2.52.0

View File

@ -294,7 +294,7 @@
Summary: Library providing a simple virtualization API
Name: libvirt
Version: 11.10.0
Release: 1%{?dist}%{?extra_release}
Release: 2%{?dist}%{?extra_release}
License: GPL-2.0-or-later AND LGPL-2.1-only AND LGPL-2.1-or-later AND OFL-1.1
URL: https://libvirt.org/
@ -302,6 +302,12 @@ URL: https://libvirt.org/
%define mainturl stable_updates/
%endif
Source: https://download.libvirt.org/%{?mainturl}libvirt-%{version}.tar.xz
Patch1: libvirt-qemu-tpm-Account-for-possible-migration-without-actually-sharing-storage.patch
Patch2: libvirt-tests-Test-virFileIsSharedFSOverride.patch
Patch3: libvirt-util-Fix-race-condition-in-virFileIsSharedFSType.patch
Patch4: libvirt-util-Fix-race-condition-in-virFileIsSharedFSOverride.patch
Patch5: libvirt-util-Rework-virFileIsSharedFSOverride-using-virFileCheckParents.patch
Requires: libvirt-daemon = %{version}-%{release}
Requires: libvirt-daemon-config-network = %{version}-%{release}
@ -1168,6 +1174,9 @@ MinGW Windows libvirt virtualization library.
%prep
%autosetup -S git_am -N
%autopatch
%build
%if 0%{?fedora} >= %{min_fedora} || 0%{?rhel} >= %{min_rhel}
%define supported_platform 1
@ -2689,6 +2698,13 @@ exit 0
%endif
%changelog
* Thu Dec 18 2025 Jiri Denemark <jdenemar@redhat.com> - 11.10.0-2
- qemu: tpm: Account for possible migration without actually sharing storage (RHEL-108915)
- tests: Test virFileIsSharedFSOverride (RHEL-135287)
- util: Fix race condition in virFileIsSharedFSType (RHEL-135287)
- util: Fix race condition in virFileIsSharedFSOverride (RHEL-135287)
- util: Rework virFileIsSharedFSOverride using virFileCheckParents (RHEL-135287)
* Tue Dec 2 2025 Jiri Denemark <jdenemar@redhat.com> - 11.10.0-1
- Rebased to libvirt-11.10.0 (RHEL-118197)
- The rebase also fixes the following bugs: