From nobody Fri Dec 12 14:05:56 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1765293212; cv=none; d=zohomail.com; s=zohoarc; b=dlt5RZeCOPR5+/0ddUns/zUrpaqk8yLjrg6Vi4x7GE77I0Doh0tfZ20yCMMWe2MPyNXDWMcQzwnZPNv5ybZE1YuqMD5/1SV6iaQ9REG8gchharfIIoipN5nWCiy0grn/TKATkYZxcVdSS5kM9VsCmFgrZOKMDxHKsllePgaHD3c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1765293212; h=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=TR0NWFrHYp0zb8lh7Ecm8Yn0zE7yFEzk3VGvSvjvQ0c=; b=aD51mDYEP74dOPM9TlZJlnjMdONxviauKJ1cDNktOC/9URiERdGYJ0dIDTmgwpn6KStr/XQNy7w8Yp6MIXKnZw2pt145K7TldOV8DdFAoWgUZ0l9AUjCLtkS64L+bqTo1jnjq9vzPSzNYOjBHXAE/y/c37RuX9te2D+NBox0Ihg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1765293204053240.34778253442323; Tue, 9 Dec 2025 07:13:24 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 9D789418BE; Tue, 9 Dec 2025 10:13:21 -0500 (EST) Received: from [172.19.199.80] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id A402D41BF1; Tue, 9 Dec 2025 10:12:09 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id B42D64189F; Tue, 9 Dec 2025 10:03:41 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 19FBD41998 for ; Tue, 9 Dec 2025 10:03:40 -0500 (EST) Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-578-PVcatIN_PzynnHPtea99sQ-1; Tue, 09 Dec 2025 10:03:38 -0500 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4833A1800342 for ; Tue, 9 Dec 2025 15:03:37 +0000 (UTC) Received: from orkuz (unknown [10.44.33.57]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C2F81180045B for ; Tue, 9 Dec 2025 15:03:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1765292619; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TR0NWFrHYp0zb8lh7Ecm8Yn0zE7yFEzk3VGvSvjvQ0c=; b=giux9YkI31cnLyKsQl9/aaLvfmw7f/YFJQA3Fo3vbhS/qfk5apRnyuReDE8Iff7TJZgbNR LuQ/i249Jua9JKrjq6BwnFc0/sfmoGvd/7xSzvShy7l2vygSu51ow2qg2gMNV6R8scMMJ0 F7ALfxadma6Qh5dpJCrIHKLf5p9n4H8= X-MC-Unique: PVcatIN_PzynnHPtea99sQ-1 X-Mimecast-MFC-AGG-ID: PVcatIN_PzynnHPtea99sQ_1765292617 To: devel@lists.libvirt.org Subject: [PATCH 2/4] util: Fix race condition in virFileIsSharedFSType Date: Tue, 9 Dec 2025 16:02:28 +0100 Message-ID: <5fee3f2b6337f31911390c0a94278e77b99bdbfb.1765292471.git.jdenemar@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: nz4bu7uNhOCQxtEKEZ6cqnRv4IOME8iYeX4_95-DKI8_1765292617 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: ZZIVZXUSGNBCHXFLKFFSDXJSYP6H4VN4 X-Message-ID-Hash: ZZIVZXUSGNBCHXFLKFFSDXJSYP6H4VN4 X-MailFrom: jdenemar@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Jiri Denemark via Devel Reply-To: Jiri Denemark X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1765293217201158500 Content-Type: text/plain; charset="utf-8" From: Jiri Denemark 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 --- 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 f195d02e29..b0f4041df4 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -3445,6 +3445,63 @@ virFileRemoveLastComponent(char *path) } =20 =20 +/* 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 =3D g_strdup(path); + char *p =3D NULL; + bool checkOK; + + checkOK =3D check(dirpath, opaque); + + while (!checkOK && p !=3D dirpath) { + if (!(p =3D strrchr(dirpath, G_DIR_SEPARATOR))) { + virReportSystemError(EINVAL, + _("Invalid absolute path '%1$s'"), path); + return -1; + } + + if (p =3D=3D dirpath) + *(p + 1) =3D '\0'; + else + *p =3D '\0'; + + checkOK =3D check(dirpath, opaque); + } + + if (!checkOK) + return -2; + + if (parent) + *parent =3D g_steal_pointer(&dirpath); + + return 0; +} + + static char * virFileGetExistingParent(const char *path) { @@ -3599,6 +3656,14 @@ static const struct virFileSharedFsData virFileShare= dFs[] =3D { }; =20 =20 +static bool +virFileCheckParentsStatFS(const char *path, + void *opaque) +{ + return statfs(path, (struct statfs *) opaque) =3D=3D 0; +} + + int virFileIsSharedFSType(const char *path, unsigned int fstypes) @@ -3607,11 +3672,13 @@ virFileIsSharedFSType(const char *path, struct statfs sb; long long f_type =3D 0; size_t i; + int rc; =20 - if (!(dirpath =3D virFileGetExistingParent(path))) + if ((rc =3D virFileCheckParents(path, &dirpath, + virFileCheckParentsStatFS, &sb)) =3D=3D = -1) return -1; =20 - if (statfs(dirpath, &sb) < 0) { + if (rc !=3D 0) { virReportSystemError(errno, _("cannot determine filesystem for '%1$s'"), path); --=20 2.52.0