提交 b8e659aa 编写于 作者: M Michal Privoznik

qemuDomainGetHostdevPath: Create /dev/vfio/vfio iff needed

So far, we are allowing /dev/vfio/vfio in the devices cgroup
unconditionally (and creating it in the namespace too). Even if
domain has no hostdev assignment configured. This is potential
security hole. Therefore, when starting the domain (or
hotplugging a hostdev) create & allow /dev/vfio/vfio too (if
needed).
Signed-off-by: NMichal Privoznik <mprivozn@redhat.com>
Reviewed-by: NMarc-André Lureau <marcandre.lureau@redhat.com>
上级 9d92f533
......@@ -354,7 +354,7 @@
# "/dev/null", "/dev/full", "/dev/zero",
# "/dev/random", "/dev/urandom",
# "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
# "/dev/rtc","/dev/hpet", "/dev/vfio/vfio"
# "/dev/rtc","/dev/hpet"
#]
#
# RDMA migration requires the following extra files to be added to the list:
......
......@@ -46,12 +46,13 @@ const char *const defaultDeviceACL[] = {
"/dev/null", "/dev/full", "/dev/zero",
"/dev/random", "/dev/urandom",
"/dev/ptmx", "/dev/kvm", "/dev/kqemu",
"/dev/rtc", "/dev/hpet", "/dev/vfio/vfio",
"/dev/rtc", "/dev/hpet",
NULL,
};
#define DEVICE_PTY_MAJOR 136
#define DEVICE_SND_MAJOR 116
#define DEV_VFIO "/dev/vfio/vfio"
static int
qemuSetupImagePathCgroup(virDomainObjPtr vm,
......@@ -265,26 +266,31 @@ qemuSetupHostdevCgroup(virDomainObjPtr vm,
virDomainHostdevDefPtr dev)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
char *path = NULL;
int perms;
int ret = -1;
char **path = NULL;
int *perms = NULL;
size_t i, npaths = 0;
int rv, ret = -1;
if (qemuDomainGetHostdevPath(dev, &path, &perms) < 0)
if (qemuDomainGetHostdevPath(dev, &npaths, &path, &perms) < 0)
goto cleanup;
if (!path) {
/* There's no path that we need to allow. Claim success. */
ret = 0;
goto cleanup;
for (i = 0; i < npaths; i++) {
VIR_DEBUG("Cgroup allow %s perms=%d", path[i], perms[i]);
rv = virCgroupAllowDevicePath(priv->cgroup, path[i], perms[i], false);
virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path[i],
virCgroupGetDevicePermsString(perms[i]),
ret == 0);
if (rv < 0)
goto cleanup;
}
VIR_DEBUG("Cgroup allow %s perms=%d", path, perms);
ret = virCgroupAllowDevicePath(priv->cgroup, path, perms, false);
virDomainAuditCgroupPath(vm, priv->cgroup, "allow", path,
virCgroupGetDevicePermsString(perms), ret == 0);
ret = 0;
cleanup:
for (i = 0; i < npaths; i++)
VIR_FREE(path[i]);
VIR_FREE(path);
VIR_FREE(perms);
return ret;
}
......@@ -312,6 +318,7 @@ qemuTeardownHostdevCgroup(virDomainObjPtr vm,
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
if (pcisrc->backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
int rv;
size_t i, vfios = 0;
pci = virPCIDeviceNew(pcisrc->addr.domain,
pcisrc->addr.bus,
......@@ -330,6 +337,26 @@ qemuTeardownHostdevCgroup(virDomainObjPtr vm,
"deny", path, "rwm", rv == 0);
if (rv < 0)
goto cleanup;
/* If this is the last hostdev with VFIO backend deny
* /dev/vfio/vfio too. */
for (i = 0; i < vm->def->nhostdevs; i++) {
virDomainHostdevDefPtr tmp = vm->def->hostdevs[i];
if (tmp->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
tmp->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
tmp->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO)
vfios++;
}
if (vfios == 0) {
VIR_DEBUG("Cgroup deny " DEV_VFIO " for PCI device assignment");
rv = virCgroupDenyDevicePath(priv->cgroup, DEV_VFIO,
VIR_CGROUP_DEVICE_RWM, false);
virDomainAuditCgroupPath(vm, priv->cgroup,
"deny", DEV_VFIO, "rwm", rv == 0);
if (rv < 0)
goto cleanup;
}
}
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
......
......@@ -107,6 +107,7 @@ VIR_ENUM_IMPL(qemuDomainNamespace, QEMU_DOMAIN_NS_LAST,
#define PROC_MOUNTS "/proc/mounts"
#define DEVPREFIX "/dev/"
#define DEV_VFIO "/dev/vfio/vfio"
struct _qemuDomainLogContext {
......@@ -6846,18 +6847,24 @@ qemuDomainSupportsVideoVga(virDomainVideoDefPtr video,
/**
* qemuDomainGetHostdevPath:
* @dev: host device definition
* @npaths: number of items in @path and @perms arrays
* @path: resulting path to @dev
* @perms: Optional pointer to VIR_CGROUP_DEVICE_* perms
*
* For given device @dev fetch its host path and store it at @path. Optionally,
* caller can get @perms on the path (e.g. rw/ro).
* For given device @dev fetch its host path and store it at
* @path. If a device requires other paths to be present/allowed
* they are stored in the @path array after the actual path.
* Optionally, caller can get @perms on the path (e.g. rw/ro).
*
* The caller is responsible for freeing the memory.
*
* Returns 0 on success, -1 otherwise.
*/
int
qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
char **path,
int *perms)
size_t *npaths,
char ***path,
int **perms)
{
int ret = -1;
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
......@@ -6870,8 +6877,13 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
virSCSIVHostDevicePtr host = NULL;
char *tmpPath = NULL;
bool freeTmpPath = false;
bool includeVFIO = false;
char **tmpPaths = NULL;
int *tmpPerms = NULL;
size_t i, tmpNpaths = 0;
int perm = 0;
*path = NULL;
*npaths = 0;
switch ((virDomainHostdevMode) dev->mode) {
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
......@@ -6888,8 +6900,9 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
if (!(tmpPath = virPCIDeviceGetIOMMUGroupDev(pci)))
goto cleanup;
freeTmpPath = true;
if (perms)
*perms = VIR_CGROUP_DEVICE_RW;
perm = VIR_CGROUP_DEVICE_RW;
includeVFIO = true;
}
break;
......@@ -6904,8 +6917,7 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
if (!(tmpPath = (char *) virUSBDeviceGetPath(usb)))
goto cleanup;
if (perms)
*perms = VIR_CGROUP_DEVICE_RW;
perm = VIR_CGROUP_DEVICE_RW;
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
......@@ -6930,9 +6942,8 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
if (!(tmpPath = (char *) virSCSIDeviceGetPath(scsi)))
goto cleanup;
if (perms)
*perms = virSCSIDeviceGetReadonly(scsi) ?
VIR_CGROUP_DEVICE_READ : VIR_CGROUP_DEVICE_RW;
perm = virSCSIDeviceGetReadonly(scsi) ?
VIR_CGROUP_DEVICE_READ : VIR_CGROUP_DEVICE_RW;
}
break;
......@@ -6944,8 +6955,7 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
if (!(tmpPath = (char *) virSCSIVHostDeviceGetPath(host)))
goto cleanup;
if (perms)
*perms = VIR_CGROUP_DEVICE_RW;
perm = VIR_CGROUP_DEVICE_RW;
}
break;
}
......@@ -6961,11 +6971,40 @@ qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
break;
}
if (VIR_STRDUP(*path, tmpPath) < 0)
goto cleanup;
if (tmpPath) {
size_t toAlloc = 1;
if (includeVFIO)
toAlloc = 2;
if (VIR_ALLOC_N(tmpPaths, toAlloc) < 0 ||
VIR_ALLOC_N(tmpPerms, toAlloc) < 0 ||
VIR_STRDUP(tmpPaths[0], tmpPath) < 0)
goto cleanup;
tmpNpaths = toAlloc;
tmpPerms[0] = perm;
if (includeVFIO) {
if (VIR_STRDUP(tmpPaths[1], DEV_VFIO) < 0)
goto cleanup;
tmpPerms[1] = VIR_CGROUP_DEVICE_RW;
}
}
*npaths = tmpNpaths;
tmpNpaths = 0;
*path = tmpPaths;
tmpPaths = NULL;
if (perms) {
*perms = tmpPerms;
tmpPerms = NULL;
}
ret = 0;
cleanup:
for (i = 0; i < tmpNpaths; i++)
VIR_FREE(tmpPaths[i]);
VIR_FREE(tmpPaths);
VIR_FREE(tmpPerms);
virPCIDeviceFree(pci);
virUSBDeviceFree(usb);
virSCSIDeviceFree(scsi);
......@@ -7369,22 +7408,21 @@ qemuDomainSetupHostdev(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
const char *devPath)
{
int ret = -1;
char *path = NULL;
char **path = NULL;
size_t i, npaths = 0;
if (qemuDomainGetHostdevPath(dev, &path, NULL) < 0)
if (qemuDomainGetHostdevPath(dev, &npaths, &path, NULL) < 0)
goto cleanup;
if (!path) {
/* There's no /dev device that we need to create. Claim success. */
ret = 0;
goto cleanup;
for (i = 0; i < npaths; i++) {
if (qemuDomainCreateDevice(path[i], devPath, false) < 0)
goto cleanup;
}
if (qemuDomainCreateDevice(path, devPath, false) < 0)
goto cleanup;
ret = 0;
cleanup:
for (i = 0; i < npaths; i++)
VIR_FREE(path[i]);
VIR_FREE(path);
return ret;
}
......@@ -8018,26 +8056,26 @@ qemuDomainNamespaceSetupHostdev(virQEMUDriverPtr driver,
virDomainHostdevDefPtr hostdev)
{
int ret = -1;
char *path = NULL;
char **path = NULL;
size_t i, npaths = 0;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
return 0;
if (qemuDomainGetHostdevPath(hostdev, &path, NULL) < 0)
if (qemuDomainGetHostdevPath(hostdev, &npaths, &path, NULL) < 0)
goto cleanup;
if (!path) {
/* There's no /dev device that we need to create. Claim success. */
ret = 0;
for (i = 0; i < npaths; i++) {
if (qemuDomainAttachDeviceMknod(driver,
vm,
path[i]) < 0)
goto cleanup;
}
if (qemuDomainAttachDeviceMknod(driver,
vm,
path) < 0)
goto cleanup;
ret = 0;
cleanup:
for (i = 0; i < npaths; i++)
VIR_FREE(path[i]);
VIR_FREE(path);
return ret;
}
......@@ -8049,25 +8087,25 @@ qemuDomainNamespaceTeardownHostdev(virQEMUDriverPtr driver,
virDomainHostdevDefPtr hostdev)
{
int ret = -1;
char *path = NULL;
char **path = NULL;
size_t i, npaths = 0;
if (!qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
return 0;
if (qemuDomainGetHostdevPath(hostdev, &path, NULL) < 0)
if (qemuDomainGetHostdevPath(hostdev, &npaths, &path, NULL) < 0)
goto cleanup;
if (!path) {
/* There's no /dev device that we need to create. Claim success. */
ret = 0;
goto cleanup;
}
if (qemuDomainDetachDeviceUnlink(driver, vm, path) < 0)
/* Don't remove other paths than for the @hostdev itself.
* They might be still in use by other devices. */
if (npaths > 0 &&
qemuDomainDetachDeviceUnlink(driver, vm, path[0]) < 0)
goto cleanup;
ret = 0;
cleanup:
for (i = 0; i < npaths; i++)
VIR_FREE(path[i]);
VIR_FREE(path);
return ret;
}
......
......@@ -803,8 +803,9 @@ bool qemuDomainSupportsVideoVga(virDomainVideoDefPtr video,
virQEMUCapsPtr qemuCaps);
int qemuDomainGetHostdevPath(virDomainHostdevDefPtr dev,
char **path,
int *perms);
size_t *npaths,
char ***path,
int **perms);
int qemuDomainBuildNamespace(virQEMUDriverPtr driver,
virDomainObjPtr vm);
......
......@@ -55,7 +55,6 @@ module Test_libvirtd_qemu =
{ "8" = "/dev/kqemu" }
{ "9" = "/dev/rtc" }
{ "10" = "/dev/hpet" }
{ "11" = "/dev/vfio/vfio" }
}
{ "save_image_format" = "raw" }
{ "dump_image_format" = "raw" }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册