From: Nathan Chen <nathanc@nvidia.com>
When launching a qemu VM with the iommufd feature enabled for VFIO
hostdevs:
- Do not allow cgroup, namespace, and seclabel access to VFIO
paths (/dev/vfio/vfio and /dev/vfio/<iommugroup>)
- Allow access to iommufd paths (/dev/iommu and
/dev/vfio/devices/vfio*) for AppArmor, SELinux, and DAC
Signed-off-by: Nathan Chen <nathanc@nvidia.com>
---
src/qemu/qemu_cgroup.c | 26 +++++++-------
src/qemu/qemu_namespace.c | 16 +++++----
src/security/security_apparmor.c | 33 ++++++++++++++----
src/security/security_dac.c | 60 ++++++++++++++++++++++++++------
src/security/security_selinux.c | 58 ++++++++++++++++++++++++------
src/security/virt-aa-helper.c | 32 +++++++++++++----
6 files changed, 172 insertions(+), 53 deletions(-)
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c
index 7dadef0739..7190a4f80f 100644
--- a/src/qemu/qemu_cgroup.c
+++ b/src/qemu/qemu_cgroup.c
@@ -479,21 +479,23 @@ qemuSetupHostdevCgroup(virDomainObj *vm,
g_autofree char *path = NULL;
int perms;
- if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
- return 0;
+ if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_DEVICES))
+ return 0;
- if (qemuDomainGetHostdevPath(dev, &path, &perms) < 0)
- return -1;
+ if (qemuDomainGetHostdevPath(dev, &path, &perms) < 0)
+ return -1;
- if (path &&
- qemuCgroupAllowDevicePath(vm, path, perms, false) < 0) {
- return -1;
- }
+ if (path &&
+ qemuCgroupAllowDevicePath(vm, path, perms, false) < 0) {
+ return -1;
+ }
- if (virHostdevNeedsVFIO(dev) &&
- qemuCgroupAllowDevicePath(vm, QEMU_DEV_VFIO,
- VIR_CGROUP_DEVICE_RW, false) < 0) {
- return -1;
+ if (virHostdevNeedsVFIO(dev) &&
+ qemuCgroupAllowDevicePath(vm, QEMU_DEV_VFIO,
+ VIR_CGROUP_DEVICE_RW, false) < 0) {
+ return -1;
+ }
}
return 0;
diff --git a/src/qemu/qemu_namespace.c b/src/qemu/qemu_namespace.c
index c689cc3e40..907b2773cf 100644
--- a/src/qemu/qemu_namespace.c
+++ b/src/qemu/qemu_namespace.c
@@ -345,15 +345,17 @@ qemuDomainSetupHostdev(virDomainObj *vm,
{
g_autofree char *path = NULL;
- if (qemuDomainGetHostdevPath(hostdev, &path, NULL) < 0)
- return -1;
+ if (hostdev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ if (qemuDomainGetHostdevPath(hostdev, &path, NULL) < 0)
+ return -1;
- if (path)
- *paths = g_slist_prepend(*paths, g_steal_pointer(&path));
+ if (path)
+ *paths = g_slist_prepend(*paths, g_steal_pointer(&path));
- if (virHostdevNeedsVFIO(hostdev) &&
- (!hotplug || !qemuDomainNeedsVFIO(vm->def)))
- *paths = g_slist_prepend(*paths, g_strdup(QEMU_DEV_VFIO));
+ if (virHostdevNeedsVFIO(hostdev) &&
+ (!hotplug || !qemuDomainNeedsVFIO(vm->def)))
+ *paths = g_slist_prepend(*paths, g_strdup(QEMU_DEV_VFIO));
+ }
return 0;
}
diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
index 68ac39611f..999275dac1 100644
--- a/src/security/security_apparmor.c
+++ b/src/security/security_apparmor.c
@@ -848,14 +848,33 @@ AppArmorSetSecurityHostdevLabel(virSecurityManager *mgr,
goto done;
if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
- char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
-
- if (!vfioGroupDev) {
- virPCIDeviceFree(pci);
- goto done;
+ if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
+
+ if (!vfioGroupDev) {
+ virPCIDeviceFree(pci);
+ goto done;
+ }
+ ret = AppArmorSetSecurityPCILabel(pci, vfioGroupDev, ptr);
+ VIR_FREE(vfioGroupDev);
+ } else {
+ g_autofree char *vfiofdDev = NULL;
+ const char *iommufdDir = "/dev/iommu";
+
+ if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
+ return -1;
+
+ if (!virFileExists(iommufdDir))
+ return -1;
+
+ ret = AppArmorSetSecurityPCILabel(pci, vfiofdDev, ptr);
+ if (ret)
+ return ret;
+
+ ret = AppArmorSetSecurityPCILabel(pci, iommufdDir, ptr);
+ if (ret)
+ return ret;
}
- ret = AppArmorSetSecurityPCILabel(pci, vfioGroupDev, ptr);
- VIR_FREE(vfioGroupDev);
} else {
ret = virPCIDeviceFileIterate(pci, AppArmorSetSecurityPCILabel, ptr);
}
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index 2f788b872a..09e26033ac 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -1282,14 +1282,33 @@ virSecurityDACSetHostdevLabel(virSecurityManager *mgr,
return -1;
if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
- g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
+ if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
- if (!vfioGroupDev)
- return -1;
+ if (!vfioGroupDev)
+ return -1;
+
+ ret = virSecurityDACSetHostdevLabelHelper(vfioGroupDev,
+ false,
+ &cbdata);
+ } else {
+ g_autofree char *vfiofdDev = NULL;
+ const char *iommufdDir = "/dev/iommu";
+
+ if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
+ return -1;
- ret = virSecurityDACSetHostdevLabelHelper(vfioGroupDev,
- false,
- &cbdata);
+ if (!virFileExists(iommufdDir))
+ return -1;
+
+ ret = virSecurityDACSetHostdevLabelHelper(vfiofdDev, false, &cbdata);
+ if (ret)
+ return ret;
+
+ ret = virSecurityDACSetHostdevLabelHelper(iommufdDir, false, &cbdata);
+ if (ret)
+ return ret;
+ }
} else {
ret = virPCIDeviceFileIterate(pci,
virSecurityDACSetPCILabel,
@@ -1443,13 +1462,34 @@ virSecurityDACRestoreHostdevLabel(virSecurityManager *mgr,
return -1;
if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
- g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
+ if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
- if (!vfioGroupDev)
- return -1;
+ if (!vfioGroupDev)
+ return -1;
- ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
+ ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
vfioGroupDev, false);
+ } else {
+ g_autofree char *vfiofdDev = NULL;
+ const char *iommufdDir = "/dev/iommu";
+
+ if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
+ return -1;
+
+ if (!virFileExists(iommufdDir))
+ return -1;
+
+ ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
+ vfiofdDev, false);
+ if (ret)
+ return ret;
+
+ ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
+ iommufdDir, false);
+ if (ret)
+ return ret;
+ }
} else {
ret = virPCIDeviceFileIterate(pci, virSecurityDACRestorePCILabel, mgr);
}
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 2f3cc274a5..1dd0a9706a 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -2256,14 +2256,33 @@ virSecuritySELinuxSetHostdevSubsysLabel(virSecurityManager *mgr,
return -1;
if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
- g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
+ if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
- if (!vfioGroupDev)
- return -1;
+ if (!vfioGroupDev)
+ return -1;
+
+ ret = virSecuritySELinuxSetHostdevLabelHelper(vfioGroupDev,
+ false,
+ &data);
+ } else {
+ g_autofree char *vfiofdDev = NULL;
+ const char *iommufdDir = "/dev/iommu";
+
+ if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
+ return -1;
- ret = virSecuritySELinuxSetHostdevLabelHelper(vfioGroupDev,
- false,
- &data);
+ if (!virFileExists(iommufdDir))
+ return -1;
+
+ ret = virSecuritySELinuxSetHostdevLabelHelper(vfiofdDev, false, &data);
+ if (ret)
+ return ret;
+
+ ret = virSecuritySELinuxSetHostdevLabelHelper(iommufdDir, false, &data);
+ if (ret)
+ return ret;
+ }
} else {
ret = virPCIDeviceFileIterate(pci, virSecuritySELinuxSetPCILabel, &data);
}
@@ -2491,12 +2510,31 @@ virSecuritySELinuxRestoreHostdevSubsysLabel(virSecurityManager *mgr,
return -1;
if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
- g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
+ if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
+ g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
- if (!vfioGroupDev)
- return -1;
+ if (!vfioGroupDev)
+ return -1;
+
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, vfioGroupDev, false, false);
+ } else {
+ g_autofree char *vfiofdDev = NULL;
+ const char *iommufdDir = "/dev/iommu";
+
+ if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
+ return -1;
- ret = virSecuritySELinuxRestoreFileLabel(mgr, vfioGroupDev, false, false);
+ if (!virFileExists(iommufdDir))
+ return -1;
+
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, vfiofdDev, false, false);
+ if (ret)
+ return ret;
+
+ ret = virSecuritySELinuxRestoreFileLabel(mgr, iommufdDir, false, false);
+ if (ret)
+ return ret;
+ }
} else {
ret = virPCIDeviceFileIterate(pci, virSecuritySELinuxRestorePCILabel, mgr);
}
diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c
index de0a826063..5b320fbc89 100644
--- a/src/security/virt-aa-helper.c
+++ b/src/security/virt-aa-helper.c
@@ -1114,8 +1114,9 @@ get_files(vahControl * ctl)
virDeviceHostdevPCIDriverName driverName = dev->source.subsys.u.pci.driver.name;
- if (driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO ||
- driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_DEFAULT) {
+ if ((driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO ||
+ driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_DEFAULT) &&
+ dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
needsVfio = true;
}
@@ -1348,6 +1349,7 @@ get_files(vahControl * ctl)
virBufferAddLit(&buf, " \"/dev/vfio/vfio\" rw,\n");
virBufferAddLit(&buf, " \"/dev/vfio/[0-9]*\" rw,\n");
}
+
if (needsgl) {
/* if using gl all sorts of further dri related paths will be needed */
virBufferAddLit(&buf, " # DRI/Mesa/(e)GL config and driver paths\n");
@@ -1385,9 +1387,18 @@ get_files(vahControl * ctl)
}
}
- if (ctl->newfile &&
- vah_add_file(&buf, ctl->newfile, "rwk") != 0) {
- return -1;
+ if (ctl->newfile) {
+ const char *perms = "rwk";
+
+ /* VFIO and iommufd devices need mmap permission */
+ if (STRPREFIX(ctl->newfile, "/dev/vfio/devices/vfio") ||
+ STREQ(ctl->newfile, "/dev/iommu")) {
+ perms = "rwm";
+ }
+
+ if (vah_add_file(&buf, ctl->newfile, perms) != 0) {
+ return -1;
+ }
}
ctl->files = virBufferContentAndReset(&buf);
@@ -1561,8 +1572,15 @@ main(int argc, char **argv)
}
}
if (ctl->append && ctl->newfile) {
- if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
- goto cleanup;
+ const char *perms = "rwk";
+
+ if (STRPREFIX(ctl->newfile, "/dev/vfio/devices/vfio") ||
+ STREQ(ctl->newfile, "/dev/iommu")) {
+ perms = "rwm";
+ }
+
+ if (vah_add_file(&buf, ctl->newfile, perms) != 0)
+ return -1;
} else {
if (ctl->def->virtType == VIR_DOMAIN_VIRT_QEMU ||
ctl->def->virtType == VIR_DOMAIN_VIRT_KQEMU ||
--
2.43.0
On Thu, Dec 18, 2025 at 06:19:24PM -0800, Nathan Chen via Devel wrote:
> From: Nathan Chen <nathanc@nvidia.com>
>
> When launching a qemu VM with the iommufd feature enabled for VFIO
> hostdevs:
> - Do not allow cgroup, namespace, and seclabel access to VFIO
> paths (/dev/vfio/vfio and /dev/vfio/<iommugroup>)
> - Allow access to iommufd paths (/dev/iommu and
> /dev/vfio/devices/vfio*) for AppArmor, SELinux, and DAC
>
> Signed-off-by: Nathan Chen <nathanc@nvidia.com>
> ---
> src/qemu/qemu_cgroup.c | 26 +++++++-------
> src/qemu/qemu_namespace.c | 16 +++++----
> src/security/security_apparmor.c | 33 ++++++++++++++----
> src/security/security_dac.c | 60 ++++++++++++++++++++++++++------
> src/security/security_selinux.c | 58 ++++++++++++++++++++++++------
> src/security/virt-aa-helper.c | 32 +++++++++++++----
> 6 files changed, 172 insertions(+), 53 deletions(-)
snip
> diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
> index 68ac39611f..999275dac1 100644
> --- a/src/security/security_apparmor.c
> +++ b/src/security/security_apparmor.c
> @@ -848,14 +848,33 @@ AppArmorSetSecurityHostdevLabel(virSecurityManager *mgr,
> goto done;
>
> if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
> - char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
> -
> - if (!vfioGroupDev) {
> - virPCIDeviceFree(pci);
> - goto done;
> + if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
> + char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
> +
> + if (!vfioGroupDev) {
> + virPCIDeviceFree(pci);
> + goto done;
> + }
> + ret = AppArmorSetSecurityPCILabel(pci, vfioGroupDev, ptr);
> + VIR_FREE(vfioGroupDev);
> + } else {
> + g_autofree char *vfiofdDev = NULL;
> + const char *iommufdDir = "/dev/iommu";
So we declare / use this path 11 times by the end of this series
$ git grep /dev/iommu src/
src/qemu/qemu_process.c: * Opens /dev/iommu file descriptor for the VM.
src/qemu/qemu_process.c: if ((fd = open("/dev/iommu", O_RDWR | O_CLOEXEC)) < 0) {
src/qemu/qemu_process.c: _("IOMMU FD support requires /dev/iommu device"));
src/qemu/qemu_process.c: _("cannot open /dev/iommu"));
src/security/security_apparmor.c: const char *iommufdDir = "/dev/iommu";
src/security/security_dac.c: const char *iommufdDir = "/dev/iommu";
src/security/security_dac.c: const char *iommufdDir = "/dev/iommu";
src/security/security_selinux.c: const char *iommufdDir = "/dev/iommu";
src/security/security_selinux.c: const char *iommufdDir = "/dev/iommu";
src/security/virt-aa-helper.c: STREQ(ctl->newfile, "/dev/iommu")) {
src/security/virt-aa-helper.c: STREQ(ctl->newfile, "/dev/iommu")) {
how about we declare VIR_IOMMU_DEV_PATH in src/util/viriommu.h and
use that throughout.
> +
> + if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
> + return -1;
> +
> + if (!virFileExists(iommufdDir))
> + return -1;
...and even add 'bool virIOMMUFDSupported(void)' that does this
virFileExists check.
> +
> + ret = AppArmorSetSecurityPCILabel(pci, vfiofdDev, ptr);
> + if (ret)
> + return ret;
> +
> + ret = AppArmorSetSecurityPCILabel(pci, iommufdDir, ptr);
> + if (ret)
> + return ret;
> }
> - ret = AppArmorSetSecurityPCILabel(pci, vfioGroupDev, ptr);
> - VIR_FREE(vfioGroupDev);
> } else {
> ret = virPCIDeviceFileIterate(pci, AppArmorSetSecurityPCILabel, ptr);
> }
> diff --git a/src/security/security_dac.c b/src/security/security_dac.c
> index 2f788b872a..09e26033ac 100644
> --- a/src/security/security_dac.c
> +++ b/src/security/security_dac.c
> @@ -1282,14 +1282,33 @@ virSecurityDACSetHostdevLabel(virSecurityManager *mgr,
> return -1;
>
> if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
> - g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
> + if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
> + g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
>
> - if (!vfioGroupDev)
> - return -1;
> + if (!vfioGroupDev)
> + return -1;
> +
> + ret = virSecurityDACSetHostdevLabelHelper(vfioGroupDev,
> + false,
> + &cbdata);
> + } else {
> + g_autofree char *vfiofdDev = NULL;
> + const char *iommufdDir = "/dev/iommu";
> +
> + if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
> + return -1;
>
> - ret = virSecurityDACSetHostdevLabelHelper(vfioGroupDev,
> - false,
> - &cbdata);
> + if (!virFileExists(iommufdDir))
> + return -1;
> +
> + ret = virSecurityDACSetHostdevLabelHelper(vfiofdDev, false, &cbdata);
> + if (ret)
> + return ret;
> +
> + ret = virSecurityDACSetHostdevLabelHelper(iommufdDir, false, &cbdata);
> + if (ret)
> + return ret;
> + }
> } else {
> ret = virPCIDeviceFileIterate(pci,
> virSecurityDACSetPCILabel,
> @@ -1443,13 +1462,34 @@ virSecurityDACRestoreHostdevLabel(virSecurityManager *mgr,
> return -1;
>
> if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
> - g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
> + if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
> + g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
>
> - if (!vfioGroupDev)
> - return -1;
> + if (!vfioGroupDev)
> + return -1;
>
> - ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
> + ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
> vfioGroupDev, false);
> + } else {
> + g_autofree char *vfiofdDev = NULL;
> + const char *iommufdDir = "/dev/iommu";
> +
> + if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
> + return -1;
> +
> + if (!virFileExists(iommufdDir))
> + return -1;
> +
> + ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
> + vfiofdDev, false);
> + if (ret)
> + return ret;
> +
> + ret = virSecurityDACRestoreFileLabelInternal(mgr, NULL,
> + iommufdDir, false);
> + if (ret)
> + return ret;
> + }
> } else {
> ret = virPCIDeviceFileIterate(pci, virSecurityDACRestorePCILabel, mgr);
> }
> diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
> index 2f3cc274a5..1dd0a9706a 100644
> --- a/src/security/security_selinux.c
> +++ b/src/security/security_selinux.c
> @@ -2256,14 +2256,33 @@ virSecuritySELinuxSetHostdevSubsysLabel(virSecurityManager *mgr,
> return -1;
>
> if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
> - g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
> + if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
> + g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
>
> - if (!vfioGroupDev)
> - return -1;
> + if (!vfioGroupDev)
> + return -1;
> +
> + ret = virSecuritySELinuxSetHostdevLabelHelper(vfioGroupDev,
> + false,
> + &data);
> + } else {
> + g_autofree char *vfiofdDev = NULL;
> + const char *iommufdDir = "/dev/iommu";
> +
> + if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
> + return -1;
>
> - ret = virSecuritySELinuxSetHostdevLabelHelper(vfioGroupDev,
> - false,
> - &data);
> + if (!virFileExists(iommufdDir))
> + return -1;
> +
> + ret = virSecuritySELinuxSetHostdevLabelHelper(vfiofdDev, false, &data);
> + if (ret)
> + return ret;
> +
> + ret = virSecuritySELinuxSetHostdevLabelHelper(iommufdDir, false, &data);
> + if (ret)
> + return ret;
> + }
> } else {
> ret = virPCIDeviceFileIterate(pci, virSecuritySELinuxSetPCILabel, &data);
> }
> @@ -2491,12 +2510,31 @@ virSecuritySELinuxRestoreHostdevSubsysLabel(virSecurityManager *mgr,
> return -1;
>
> if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
> - g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
> + if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
> + g_autofree char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
>
> - if (!vfioGroupDev)
> - return -1;
> + if (!vfioGroupDev)
> + return -1;
> +
> + ret = virSecuritySELinuxRestoreFileLabel(mgr, vfioGroupDev, false, false);
> + } else {
> + g_autofree char *vfiofdDev = NULL;
> + const char *iommufdDir = "/dev/iommu";
> +
> + if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
> + return -1;
>
> - ret = virSecuritySELinuxRestoreFileLabel(mgr, vfioGroupDev, false, false);
> + if (!virFileExists(iommufdDir))
> + return -1;
> +
> + ret = virSecuritySELinuxRestoreFileLabel(mgr, vfiofdDev, false, false);
> + if (ret)
> + return ret;
> +
> + ret = virSecuritySELinuxRestoreFileLabel(mgr, iommufdDir, false, false);
> + if (ret)
> + return ret;
> + }
> } else {
> ret = virPCIDeviceFileIterate(pci, virSecuritySELinuxRestorePCILabel, mgr);
> }
> diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c
> index de0a826063..5b320fbc89 100644
> --- a/src/security/virt-aa-helper.c
> +++ b/src/security/virt-aa-helper.c
> @@ -1114,8 +1114,9 @@ get_files(vahControl * ctl)
>
> virDeviceHostdevPCIDriverName driverName = dev->source.subsys.u.pci.driver.name;
>
> - if (driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO ||
> - driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_DEFAULT) {
> + if ((driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO ||
> + driverName == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_DEFAULT) &&
> + dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
> needsVfio = true;
> }
>
> @@ -1348,6 +1349,7 @@ get_files(vahControl * ctl)
> virBufferAddLit(&buf, " \"/dev/vfio/vfio\" rw,\n");
> virBufferAddLit(&buf, " \"/dev/vfio/[0-9]*\" rw,\n");
> }
> +
> if (needsgl) {
> /* if using gl all sorts of further dri related paths will be needed */
> virBufferAddLit(&buf, " # DRI/Mesa/(e)GL config and driver paths\n");
> @@ -1385,9 +1387,18 @@ get_files(vahControl * ctl)
> }
> }
>
> - if (ctl->newfile &&
> - vah_add_file(&buf, ctl->newfile, "rwk") != 0) {
> - return -1;
> + if (ctl->newfile) {
> + const char *perms = "rwk";
> +
> + /* VFIO and iommufd devices need mmap permission */
> + if (STRPREFIX(ctl->newfile, "/dev/vfio/devices/vfio") ||
> + STREQ(ctl->newfile, "/dev/iommu")) {
> + perms = "rwm";
> + }
> +
> + if (vah_add_file(&buf, ctl->newfile, perms) != 0) {
> + return -1;
> + }
> }
>
> ctl->files = virBufferContentAndReset(&buf);
> @@ -1561,8 +1572,15 @@ main(int argc, char **argv)
> }
> }
> if (ctl->append && ctl->newfile) {
> - if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
> - goto cleanup;
> + const char *perms = "rwk";
> +
> + if (STRPREFIX(ctl->newfile, "/dev/vfio/devices/vfio") ||
> + STREQ(ctl->newfile, "/dev/iommu")) {
> + perms = "rwm";
> + }
> +
> + if (vah_add_file(&buf, ctl->newfile, perms) != 0)
> + return -1;
> } else {
> if (ctl->def->virtType == VIR_DOMAIN_VIRT_QEMU ||
> ctl->def->virtType == VIR_DOMAIN_VIRT_KQEMU ||
> --
> 2.43.0
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
On 1/5/2026 8:21 AM, Daniel P. Berrangé wrote:
> On Thu, Dec 18, 2025 at 06:19:24PM -0800, Nathan Chen via Devel wrote:
>> From: Nathan Chen<nathanc@nvidia.com>
>>
>> When launching a qemu VM with the iommufd feature enabled for VFIO
>> hostdevs:
>> - Do not allow cgroup, namespace, and seclabel access to VFIO
>> paths (/dev/vfio/vfio and /dev/vfio/<iommugroup>)
>> - Allow access to iommufd paths (/dev/iommu and
>> /dev/vfio/devices/vfio*) for AppArmor, SELinux, and DAC
>>
>> Signed-off-by: Nathan Chen<nathanc@nvidia.com>
>> ---
>> src/qemu/qemu_cgroup.c | 26 +++++++-------
>> src/qemu/qemu_namespace.c | 16 +++++----
>> src/security/security_apparmor.c | 33 ++++++++++++++----
>> src/security/security_dac.c | 60 ++++++++++++++++++++++++++------
>> src/security/security_selinux.c | 58 ++++++++++++++++++++++++------
>> src/security/virt-aa-helper.c | 32 +++++++++++++----
>> 6 files changed, 172 insertions(+), 53 deletions(-)
> snip
>
>> diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c
>> index 68ac39611f..999275dac1 100644
>> --- a/src/security/security_apparmor.c
>> +++ b/src/security/security_apparmor.c
>> @@ -848,14 +848,33 @@ AppArmorSetSecurityHostdevLabel(virSecurityManager *mgr,
>> goto done;
>>
>> if (pcisrc->driver.name == VIR_DEVICE_HOSTDEV_PCI_DRIVER_NAME_VFIO) {
>> - char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
>> -
>> - if (!vfioGroupDev) {
>> - virPCIDeviceFree(pci);
>> - goto done;
>> + if (dev->source.subsys.u.pci.driver.iommufd != VIR_TRISTATE_BOOL_YES) {
>> + char *vfioGroupDev = virPCIDeviceGetIOMMUGroupDev(pci);
>> +
>> + if (!vfioGroupDev) {
>> + virPCIDeviceFree(pci);
>> + goto done;
>> + }
>> + ret = AppArmorSetSecurityPCILabel(pci, vfioGroupDev, ptr);
>> + VIR_FREE(vfioGroupDev);
>> + } else {
>> + g_autofree char *vfiofdDev = NULL;
>> + const char *iommufdDir = "/dev/iommu";
> So we declare / use this path 11 times by the end of this series
>
> $ git grep/dev/iommu src/
> src/qemu/qemu_process.c: * Opens /dev/iommu file descriptor for the VM.
> src/qemu/qemu_process.c: if ((fd = open("/dev/iommu", O_RDWR | O_CLOEXEC)) < 0) {
> src/qemu/qemu_process.c: _("IOMMU FD support requires /dev/iommu device"));
> src/qemu/qemu_process.c: _("cannot open /dev/iommu"));
> src/security/security_apparmor.c: const char *iommufdDir = "/dev/iommu";
> src/security/security_dac.c: const char *iommufdDir = "/dev/iommu";
> src/security/security_dac.c: const char *iommufdDir = "/dev/iommu";
> src/security/security_selinux.c: const char *iommufdDir = "/dev/iommu";
> src/security/security_selinux.c: const char *iommufdDir = "/dev/iommu";
> src/security/virt-aa-helper.c: STREQ(ctl->newfile, "/dev/iommu")) {
> src/security/virt-aa-helper.c: STREQ(ctl->newfile, "/dev/iommu")) {
>
>
> how about we declare VIR_IOMMU_DEV_PATH in src/util/viriommu.h and
> use that throughout.
>
Yes, that would be cleaner - I will include this in the next revision.
>> +
>> + if (virPCIDeviceGetVfioPath(&dev->source.subsys.u.pci.addr, &vfiofdDev) < 0)
>> + return -1;
>> +
>> + if (!virFileExists(iommufdDir))
>> + return -1;
> ...and even add 'bool virIOMMUFDSupported(void)' that does this
> virFileExists check.
That makes sense, I will implement this function in the next revision.
Thanks,
Nathan
© 2016 - 2026 Red Hat, Inc.