From nobody Thu Nov 12 15:08:26 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1793013347; cv=none; d=zohomail.com; s=zohoarc; b=bbLHstW2LrsDq4TzHnxL06gxzUpC3DXs+OBoYOMCK555eY9f+1xm0xQkZv2+63OKRAIInQFDEVNg6IiwnFsqjHuDiRRKIlgq/nSLc/ZYBQVrCZhMs7vKQwc//cnI9KY6fYW6W6khSvlXTbYyoMuWSv1Ul6ydrM9/DbWaS9VT4eU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1793013347; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=eLFqqkvxaJLVOuihxDZJ/zvhfGeweY/uniNfwJZG7g8=; b=CxiAlWdivYXgZMrXYkKwiORJyBy5PSRlIr7jOTwVLqylx9ouch0ZBZPyzxaAkxxy5lGB3nkexR5JFgONmp2didE9ULmcFeDvP2vDj9kU08KTZxmSwN1X3VNMla/dh5nMsMNZR8UHT8ScfiiqMpF4e5w7RNUjqMqIukegzjQQ72o= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1793013346993803.017581342904; Mon, 26 Oct 2026 04:15:46 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 6115C41A51; Mon, 26 Oct 2026 07:15:45 -0400 (EDT) Received: from [172.19.199.19] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id 7D7D541BAB; Mon, 26 Oct 2026 07:14:06 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 50D1F3F383; Mon, 26 Oct 2026 07:05:52 -0400 (EDT) Received: from mail-lf1-x132.google.com (mail-lf1-x132.google.com [IPv6:2a00:1450:4864:20::132]) (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 D96933F366 for ; Mon, 26 Oct 2026 07:05:50 -0400 (EDT) Received: by mail-lf1-x132.google.com with SMTP id 2adb3069b0e04-5be47aa555cso3174118e87.3 for ; Mon, 26 Oct 2026 04:05:50 -0700 (PDT) Received: from lenovo-legion5 (pppoe-77.220.51.219.ttel.ru. [77.220.51.219]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5bfb7a75d1bsm1477457e87.34.2026.10.26.04.05.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2026 04:05:48 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,URIBL_SBL_A autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793012749; x=1793617549; darn=lists.libvirt.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=eLFqqkvxaJLVOuihxDZJ/zvhfGeweY/uniNfwJZG7g8=; b=A6sXAESr51mOPUTLBSCzDae315sA2f+/fqGKpzjCXJuJTU7oy33oQ/OHsNno8oGR31 UCmKOmHv3TYZXm4XFVw4x4CkXt8T4i6IDmFZU4eoMPhkWEgpPkPAiWAI75uZKlFf3a/H 87ASW9RGdcDtUz9BarEblp7qE1ca8ivio/mGUf5QuWYkdkbZQp6EC0vKsy0WYA0TuXcL l4G6LUZ6Ba2MWsskYeMnCZQEh1Ynr/t5d7g1zw/wtQTx3AF8fSxu29dZdXYz4cK+7Nso rMM1+MBOX1b4pfBS9cn/dl4q6A1TymjP+HQg/qK271oykMiIlPubRANWN5onMfMCsFrz Lr1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793012749; x=1793617549; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=eLFqqkvxaJLVOuihxDZJ/zvhfGeweY/uniNfwJZG7g8=; b=NIrsxTMAEvDKKscMZPmwdxiAV9Abv6obg9xoms+Om7QRf2mxlHhmQfxjFiQkbM0jGe bGkNoXLiMHYrSfGtbGnEpVlDm+bR+Tb+RKxN+UU7By7pnrclYI2Ii5OXG8IFYyp25B9H kD415ySf/dmwQXclzoeFdbKyZFxlIqgxrBgxu7It4CQ7mu2BCE9a4QmGS+/Y/wr0xCXg JI0e3C8KX/3qSUo3OwcoCt4BZynMBZkAmrx7m5CT/KiRcA/u5XeRy3hMSfEhCTPisUY9 6qDbjMSnZ/dYt77TYDeyjAAf241CPLdoOdXFFU/XifdU1May2FJYHIKWOxVlfgeo/iOV sOxA== X-Gm-Message-State: AFq9FYL8Gw1y48k8Zm5TKthp/eJjXl649+6iI/x8hNdyGEpQon5i7nOP F16nJqm5qdgglUSlScfj1hxPSye5WjPew0lY6ckwCjtoql0adkkD6x2mzxHaPRnt X-Gm-Gg: AYBFou3O89Md3j5Mri+E2bZVypViR24Tr2JspitCOtpfOUhwF4dpvdyQ4bbiH9CDZzQ 2kjux/hufkznhGMzFyUVRloMJB58rxcubPobXv86pXf68XrkhPY3M+bdcYDRyVVuc8qFpFEMj4X C2POpFc7zKoq0TRn72tK8z1IhBHwgPeSzFH2MbcrM/XM0utG8kXb8kABibI/X5x3iwiCCjOzPKz /cgXD2pi64kuzFe1vHOMnJ6yMmf0llIVRECRSDTN1lVTgZNkzflZR2BgV4IkUB+jNQ911fQj7Ig oK3zVVEX3EjbIE8/+lLSgefhdNzfopfVs6jqqJAAZ1si61gKj/zG0ssmZCMHf5UaknaJ2XO4/ZH c8mWXHrY4aEKnaKpzjvrfFi8nh1nlSPsUZ7fxvTVSI0roRi935QHs8eRH/v9BsGWZAGIWjTkWdv spqnOWlhiQWJ/b3fB0YmIo8vNa7YOBvSkX59RxDJ6J7AZl6FQxGhBAtNkjf7Yo5is3Vwfl7fbe0 paFwR/h1ito2499aWuwKsPRMcvSwRIj5p7PEwmUTgikPHknDLZdxT4fBSqGIaJOfVEpE/+uhrmn xzUrg+oj92qm2fWutsCVc7VAD08JXqecRrZcv9jbCzFlPkjurxWCLN1dfAx1mSawMdWqr40= X-Received: by 2002:a05:6512:398e:b0:5bf:b96b:2c75 with SMTP id 2adb3069b0e04-5bfb96b2cf3mr2170957e87.37.1793012748928; Mon, 26 Oct 2026 04:05:48 -0700 (PDT) From: Yaroslav Borbat To: devel@lists.libvirt.org Subject: [PATCH v2] util: virusb: skip USB devices with an inaccessible device node Date: Mon, 26 Oct 2026 14:05:38 +0300 Message-ID: <20261026110538.208201-1-yaroslav.752@gmail.com> X-Mailer: git-send-email 2.55.0 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: yaroslav.752@gmail.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: 54IBJPS2YCZ24CGZUNW5BM4SGWBNKNS4 X-Message-ID-Hash: 54IBJPS2YCZ24CGZUNW5BM4SGWBNKNS4 X-Mailman-Approved-At: Mon, 26 Oct 2026 11:13:58 +0000 CC: mprivozn@redhat.com, jtomko@redhat.com, laine@redhat.com, Yaroslav Borbat 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: X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1793013347337158500 Content-Type: text/plain; charset="utf-8" From: Yaroslav Borbat Inside a container sysfs is usually mounted from the host and exposes all USB devices, while nodes under /dev/bus/usb/ only exist for devices passed into the container's mount namespace. virUSBDeviceSearch() enumerated devices from sysfs alone, so such a device was reported as present: a hostdev with startupPolicy=3D'optional' was not dropped and the domain failed to start. Skip devices whose /dev node is inaccessible during enumeration. virUSBDeviceFind() now ignores them for optional hostdevs and reports an explicit "not accessible in the current mount namespace" error for mandatory ones. Extend the virusb test to cover both cases. Signed-off-by: Yaroslav Borbat Resolves: https://gitlab.com/libvirt/libvirt/-/work_items/894 --- v2: - rebased onto current master (no functional changes) v1: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/message= /20260716213249.3163905-1-yaroslav.752@gmail.com/ (https://patchew.org/Libvirt/20260716213249.3163905-1-yaroslav.752@gmai= l.com/) Context: this was hit with libvirt running inside a container (deckhouse/virtualization). Container runtimes mount sysfs from the host, so /sys/bus/usb/devices/ lists every host USB device, while /dev/bus/usb/ only contains the nodes explicitly passed into the container. A self-contained Docker reproducer (no special hardware needed, uses the root hub at bus 1 / device 1) is in the issue above. src/util/virusb.c | 41 ++++++++++++++++++++++++++++++++++++----- tests/virusbmock.c | 33 ++++++++++++++++++++++++++++++++- tests/virusbtest.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/src/util/virusb.c b/src/util/virusb.c index 85ad3d58ce..1d31912971 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -120,7 +120,8 @@ virUSBDeviceSearch(unsigned int vendor, unsigned int devno, const char *port, const char *vroot, - unsigned int flags) + unsigned int flags, + char **inaccessiblePath) { g_autoptr(DIR) dir =3D NULL; bool found =3D false; @@ -198,6 +199,28 @@ virUSBDeviceSearch(unsigned int vendor, if (!usb) goto cleanup; =20 + /* In containerized environments sysfs may expose USB devices + * from the host kernel while the corresponding device node + * under /dev/bus/usb/ is not present in the current mount + * namespace. Such a device cannot be used, so skip it here. + * Remember its path so that the caller can distinguish "no + * matching device at all" from "device present on the host but + * inaccessible in our mount namespace" when reporting errors. */ + if (virUSBDeviceGetPath(usb) && + !virFileExists(virUSBDeviceGetPath(usb))) { + VIR_DEBUG("USB device %03u:%03u found in sysfs but device " + "node '%s' is not accessible in the current mount " + "namespace, skipping", + found_bus, found_devno, + virUSBDeviceGetPath(usb)); + if (inaccessiblePath && !*inaccessiblePath) + *inaccessiblePath =3D g_strdup(virUSBDeviceGetPath(usb)); + g_clear_pointer(&usb, virUSBDeviceFree); + if (found) + break; + continue; + } + if (virUSBDeviceListAdd(list, &usb) < 0) goto cleanup; =20 @@ -226,10 +249,11 @@ virUSBDeviceFind(unsigned int vendor, virUSBDeviceList **devices) { g_autoptr(virUSBDeviceList) list =3D NULL; + g_autofree char *inaccessiblePath =3D NULL; int count; =20 if (!(list =3D virUSBDeviceSearch(vendor, product, bus, devno, port, - vroot, flags))) + vroot, flags, &inaccessiblePath))) return -1; =20 count =3D list->count; @@ -240,9 +264,16 @@ virUSBDeviceFind(unsigned int vendor, return 0; } =20 - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Did not find matching USB device: vid:%1$04x, pi= d:%2$04x, bus:%3$u, device:%4$u, port:%5$s"), - vendor, product, bus, devno, port ? port : ""); + if (inaccessiblePath) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("USB device vid:%1$04x, pid:%2$04x, bus:%3$u,= device:%4$u, port:%5$s is present on the host but its device node '%6$s' i= s not accessible in the current mount namespace"), + vendor, product, bus, devno, port ? port : "", + inaccessiblePath); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Did not find matching USB device: vid:%1$04x= , pid:%2$04x, bus:%3$u, device:%4$u, port:%5$s"), + vendor, product, bus, devno, port ? port : ""); + } return -1; } =20 diff --git a/tests/virusbmock.c b/tests/virusbmock.c index c23bed4528..c67a0ba307 100644 --- a/tests/virusbmock.c +++ b/tests/virusbmock.c @@ -22,18 +22,27 @@ #include #include #include +#include #include +#include =20 #include "virmock.h" =20 #define USB_SYSFS "/sys/bus/usb" #define FAKE_USB_SYSFS "virusbtestdata/sys_bus_usb" +#define USB_DEVFS "/dev/bus/usb/" + +/* Device node of the test device that is present in the fake sysfs but is + * meant to be inaccessible in the current mount namespace (usb4, i.e. the + * root hub 1d6b:0003 on bus 4). See virusbtest.c. */ +#define INACCESSIBLE_USB_NODE USB_DEVFS "004/001" =20 static int (*real_open)(const char *pathname, int flags, ...); #if WITH___OPEN_2 static int (*real___open_2)(const char *path, int flags); #endif static DIR *(*real_opendir)(const char *name); +static int (*real_access)(const char *path, int mode); =20 static void init_syms(void) { @@ -49,6 +58,7 @@ static void init_syms(void) VIR_MOCK_REAL_INIT(__open_2); #endif VIR_MOCK_REAL_INIT(opendir); + VIR_MOCK_REAL_INIT(access); } =20 static char *get_fake_path(const char *real_path) @@ -58,7 +68,7 @@ static char *get_fake_path(const char *real_path) =20 if ((p =3D STRSKIP(real_path, USB_SYSFS))) path =3D g_strdup_printf("%s/%s/%s", abs_srcdir, FAKE_USB_SYSFS, p= ); - else if (!p) + else path =3D g_strdup(real_path); =20 return path; @@ -75,6 +85,27 @@ DIR *opendir(const char *name) return real_opendir(path); } =20 +int access(const char *path, int mode) +{ + init_syms(); + + /* virUSBDeviceSearch() checks whether the /dev/bus/usb node of a devi= ce + * found in sysfs is accessible. There are no such nodes in the test + * environment, so pretend every USB device node exists, except the one + * device that is meant to be inaccessible in the current mount namesp= ace + * (as happens inside a container). This lets us exercise the code pa= th + * without shipping empty device-node files. */ + if (STRPREFIX(path, USB_DEVFS)) { + if (STREQ(path, INACCESSIBLE_USB_NODE)) { + errno =3D ENOENT; + return -1; + } + return 0; + } + + return real_access(path, mode); +} + int open(const char *pathname, int flags, ...) { g_autofree char *path =3D NULL; diff --git a/tests/virusbtest.c b/tests/virusbtest.c index 12ac338df9..58ca769059 100644 --- a/tests/virusbtest.c +++ b/tests/virusbtest.c @@ -259,6 +259,40 @@ testUSBList(const void *opaque G_GNUC_UNUSED) } =20 =20 +/* usb4 (bus 4, device 1) is present in the fake sysfs, but the mock makes + * its /dev/bus/usb node inaccessible, i.e. the device is exposed by the h= ost + * kernel but not accessible in our mount namespace (as happens inside a + * container). Such a device must be treated as absent: skipped for + * startupPolicy 'optional' (mandatory =3D=3D false) and reported as an er= ror + * otherwise. */ +static int +testDeviceFindInaccessible(const void *opaque G_GNUC_UNUSED) +{ + g_autoptr(virUSBDeviceList) devs =3D NULL; + int rv; + + rv =3D virUSBDeviceFind(0x1d6b, 0x0003, 4, 1, NULL, NULL, false, + USB_DEVICE_FIND_BY_DEVICE, &devs); + if (rv !=3D 0 || devs) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "optional inaccessible device: expected no match " + "(rv=3D0 and no device list), got rv=3D%d", rv); + return -1; + } + + rv =3D virUSBDeviceFind(0x1d6b, 0x0003, 4, 1, NULL, NULL, true, + USB_DEVICE_FIND_BY_DEVICE, &devs); + if (rv >=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "mandatory inaccessible device: expected failure, " + "but the device was reported as available"); + return -1; + } + + return 0; +} + + static int mymain(void) { @@ -336,6 +370,10 @@ mymain(void) if (virTestRun("USB List test", testUSBList, NULL) < 0) rv =3D -1; =20 + if (virTestRun("USBDeviceFind inaccessible node", + testDeviceFindInaccessible, NULL) < 0) + rv =3D -1; + if (rv < 0) return EXIT_FAILURE; return EXIT_SUCCESS; base-commit: ad0aefa6c66599bae1ebd3eeace615e247735e71 --=20 2.55.0