aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2011-12-20 23:59:09 -0500
committerAvi Kivity <avi@redhat.com>2011-12-25 12:03:54 -0500
commit3d27e23b17010c668db311140b17bbbb70c78fb9 (patch)
treed3d87399212b2dda0bbf6616e4a580b35e7d7760
parent423873736b78f549fbfa2f715f2e4de7e6c5e1e9 (diff)
KVM: Device assignment permission checks
Only allow KVM device assignment to attach to devices which: - Are not bridges - Have BAR resources (assume others are special devices) - The user has permissions to use Assigning a bridge is a configuration error, it's not supported, and typically doesn't result in the behavior the user is expecting anyway. Devices without BAR resources are typically chipset components that also don't have host drivers. We don't want users to hold such devices captive or cause system problems by fencing them off into an iommu domain. We determine "permission to use" by testing whether the user has access to the PCI sysfs resource files. By default a normal user will not have access to these files, so it provides a good indication that an administration agent has granted the user access to the device. [Yang Bai: add missing #include] [avi: fix comment style] Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Yang Bai <hamo.by@gmail.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
-rw-r--r--Documentation/virtual/kvm/api.txt4
-rw-r--r--virt/kvm/assigned-dev.c75
2 files changed, 79 insertions, 0 deletions
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index ee2c96b3ba5a..4df9af4f6132 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1154,6 +1154,10 @@ following flags are specified:
1154The KVM_DEV_ASSIGN_ENABLE_IOMMU flag is a mandatory option to ensure 1154The KVM_DEV_ASSIGN_ENABLE_IOMMU flag is a mandatory option to ensure
1155isolation of the device. Usages not specifying this flag are deprecated. 1155isolation of the device. Usages not specifying this flag are deprecated.
1156 1156
1157Only PCI header type 0 devices with PCI BAR resources are supported by
1158device assignment. The user requesting this ioctl must have read/write
1159access to the PCI sysfs resource files associated with the device.
1160
11574.49 KVM_DEASSIGN_PCI_DEVICE 11614.49 KVM_DEASSIGN_PCI_DEVICE
1158 1162
1159Capability: KVM_CAP_DEVICE_DEASSIGNMENT 1163Capability: KVM_CAP_DEVICE_DEASSIGNMENT
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c
index a251a28f79c7..758e3b36d4cf 100644
--- a/virt/kvm/assigned-dev.c
+++ b/virt/kvm/assigned-dev.c
@@ -17,6 +17,8 @@
17#include <linux/pci.h> 17#include <linux/pci.h>
18#include <linux/interrupt.h> 18#include <linux/interrupt.h>
19#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/namei.h>
21#include <linux/fs.h>
20#include "irq.h" 22#include "irq.h"
21 23
22static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head, 24static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head,
@@ -480,12 +482,73 @@ out:
480 return r; 482 return r;
481} 483}
482 484
485/*
486 * We want to test whether the caller has been granted permissions to
487 * use this device. To be able to configure and control the device,
488 * the user needs access to PCI configuration space and BAR resources.
489 * These are accessed through PCI sysfs. PCI config space is often
490 * passed to the process calling this ioctl via file descriptor, so we
491 * can't rely on access to that file. We can check for permissions
492 * on each of the BAR resource files, which is a pretty clear
493 * indicator that the user has been granted access to the device.
494 */
495static int probe_sysfs_permissions(struct pci_dev *dev)
496{
497#ifdef CONFIG_SYSFS
498 int i;
499 bool bar_found = false;
500
501 for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++) {
502 char *kpath, *syspath;
503 struct path path;
504 struct inode *inode;
505 int r;
506
507 if (!pci_resource_len(dev, i))
508 continue;
509
510 kpath = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
511 if (!kpath)
512 return -ENOMEM;
513
514 /* Per sysfs-rules, sysfs is always at /sys */
515 syspath = kasprintf(GFP_KERNEL, "/sys%s/resource%d", kpath, i);
516 kfree(kpath);
517 if (!syspath)
518 return -ENOMEM;
519
520 r = kern_path(syspath, LOOKUP_FOLLOW, &path);
521 kfree(syspath);
522 if (r)
523 return r;
524
525 inode = path.dentry->d_inode;
526
527 r = inode_permission(inode, MAY_READ | MAY_WRITE | MAY_ACCESS);
528 path_put(&path);
529 if (r)
530 return r;
531
532 bar_found = true;
533 }
534
535 /* If no resources, probably something special */
536 if (!bar_found)
537 return -EPERM;
538
539 return 0;
540#else
541 return -EINVAL; /* No way to control the device without sysfs */
542#endif
543}
544
483static int kvm_vm_ioctl_assign_device(struct kvm *kvm, 545static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
484 struct kvm_assigned_pci_dev *assigned_dev) 546 struct kvm_assigned_pci_dev *assigned_dev)
485{ 547{
486 int r = 0, idx; 548 int r = 0, idx;
487 struct kvm_assigned_dev_kernel *match; 549 struct kvm_assigned_dev_kernel *match;
488 struct pci_dev *dev; 550 struct pci_dev *dev;
551 u8 header_type;
489 552
490 if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU)) 553 if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU))
491 return -EINVAL; 554 return -EINVAL;
@@ -516,6 +579,18 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
516 r = -EINVAL; 579 r = -EINVAL;
517 goto out_free; 580 goto out_free;
518 } 581 }
582
583 /* Don't allow bridges to be assigned */
584 pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type);
585 if ((header_type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) {
586 r = -EPERM;
587 goto out_put;
588 }
589
590 r = probe_sysfs_permissions(dev);
591 if (r)
592 goto out_put;
593
519 if (pci_enable_device(dev)) { 594 if (pci_enable_device(dev)) {
520 printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); 595 printk(KERN_INFO "%s: Could not enable PCI device\n", __func__);
521 r = -EBUSY; 596 r = -EBUSY;