History

xen/privcmd: add IOCTL_PRIVCMD_RESTRICT

The purpose if this ioctl is to allow a user of privcmd to restrict its operation such that it will no longer service arbitrary hypercalls via IOCTL_PRIVCMD_HYPERCALL, and will check for a matching domid when servicing IOCTL_PRIVCMD_DM_OP or IOCTL_PRIVCMD_MMAP*. The aim of this is to limit the attack surface for a compromised device model.

Paul Durrant committed 9 years ago

Commit 4610d241 parent ab520be2 files · +81 −9Browse files

Unified · Split

2 files
  1. drivers/xen/privcmd.c +79 −9
  2. include/uapi/xen/privcmd.h +2 −0
drivers/xen/privcmd.c+79−9
@@ -56,16 +56,25 @@ module_param_named(dm_op_buf_max_size, privcmd_dm_op_buf_max_size, uint, · 55 unmodified lines
56MODULE_PARM_DESC(dm_op_buf_max_size,56MODULE_PARM_DESC(dm_op_buf_max_size,
57 "Maximum size of a dm_op hypercall buffer");57 "Maximum size of a dm_op hypercall buffer");
58 58
59struct privcmd_data {
60 domid_t domid;
61};
62
59static int privcmd_vma_range_is_mapped(63static int privcmd_vma_range_is_mapped(
60 struct vm_area_struct *vma,64 struct vm_area_struct *vma,
61 unsigned long addr,65 unsigned long addr,
62 unsigned long nr_pages);66 unsigned long nr_pages);
63 67
64static long privcmd_ioctl_hypercall(void __user *udata)68static long privcmd_ioctl_hypercall(struct file *file, void __user *udata)
65{69{
70 struct privcmd_data *data = file->private_data;
66 struct privcmd_hypercall hypercall;71 struct privcmd_hypercall hypercall;
67 long ret;72 long ret;
68 73
74 /* Disallow arbitrary hypercalls if restricted */
75 if (data->domid != DOMID_INVALID)
76 return -EPERM;
77
69 if (copy_from_user(&hypercall, udata, sizeof(hypercall)))78 if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
70 return -EFAULT;79 return -EFAULT;
71 80
@@ -242,8 +251,9 @@ static int mmap_gfn_range(void *data, void *state) · 170 unmodified lines
242 return 0;251 return 0;
243}252}
244 253
245static long privcmd_ioctl_mmap(void __user *udata)254static long privcmd_ioctl_mmap(struct file *file, void __user *udata)
246{255{
256 struct privcmd_data *data = file->private_data;
247 struct privcmd_mmap mmapcmd;257 struct privcmd_mmap mmapcmd;
248 struct mm_struct *mm = current->mm;258 struct mm_struct *mm = current->mm;
249 struct vm_area_struct *vma;259 struct vm_area_struct *vma;
@@ -258,6 +268,10 @@ static long privcmd_ioctl_mmap(void __user *udata) · 8 unmodified lines
258 if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd)))268 if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd)))
259 return -EFAULT;269 return -EFAULT;
260 270
271 /* If restriction is in place, check the domid matches */
272 if (data->domid != DOMID_INVALID && data->domid != mmapcmd.dom)
273 return -EPERM;
274
261 rc = gather_array(&pagelist,275 rc = gather_array(&pagelist,
262 mmapcmd.num, sizeof(struct privcmd_mmap_entry),276 mmapcmd.num, sizeof(struct privcmd_mmap_entry),
263 mmapcmd.entry);277 mmapcmd.entry);
@@ -429,8 +443,10 @@ static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs) · 165 unmodified lines
429 443
430static const struct vm_operations_struct privcmd_vm_ops;444static const struct vm_operations_struct privcmd_vm_ops;
431 445
432static long privcmd_ioctl_mmap_batch(void __user *udata, int version)446static long privcmd_ioctl_mmap_batch(
447 struct file *file, void __user *udata, int version)
433{448{
449 struct privcmd_data *data = file->private_data;
434 int ret;450 int ret;
435 struct privcmd_mmapbatch_v2 m;451 struct privcmd_mmapbatch_v2 m;
436 struct mm_struct *mm = current->mm;452 struct mm_struct *mm = current->mm;
@@ -459,6 +475,10 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) · 22 unmodified lines
459 return -EINVAL;475 return -EINVAL;
460 }476 }
461 477
478 /* If restriction is in place, check the domid matches */
479 if (data->domid != DOMID_INVALID && data->domid != m.dom)
480 return -EPERM;
481
462 nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE);482 nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE);
463 if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT)))483 if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT)))
464 return -EINVAL;484 return -EINVAL;
@@ -603,8 +623,9 @@ static void unlock_pages(struct page *pages[], unsigned int nr_pages) · 138 unmodified lines
603 }623 }
604}624}
605 625
606static long privcmd_ioctl_dm_op(void __user *udata)626static long privcmd_ioctl_dm_op(struct file *file, void __user *udata)
607{627{
628 struct privcmd_data *data = file->private_data;
608 struct privcmd_dm_op kdata;629 struct privcmd_dm_op kdata;
609 struct privcmd_dm_op_buf *kbufs;630 struct privcmd_dm_op_buf *kbufs;
610 unsigned int nr_pages = 0;631 unsigned int nr_pages = 0;
@@ -616,6 +637,10 @@ static long privcmd_ioctl_dm_op(void __user *udata) · 5 unmodified lines
616 if (copy_from_user(&kdata, udata, sizeof(kdata)))637 if (copy_from_user(&kdata, udata, sizeof(kdata)))
617 return -EFAULT;638 return -EFAULT;
618 639
640 /* If restriction is in place, check the domid matches */
641 if (data->domid != DOMID_INVALID && data->domid != kdata.dom)
642 return -EPERM;
643
619 if (kdata.num == 0)644 if (kdata.num == 0)
620 return 0;645 return 0;
621 646
@@ -683,6 +708,23 @@ out: · 61 unmodified lines
683 return rc;708 return rc;
684}709}
685 710
711static long privcmd_ioctl_restrict(struct file *file, void __user *udata)
712{
713 struct privcmd_data *data = file->private_data;
714 domid_t dom;
715
716 if (copy_from_user(&dom, udata, sizeof(dom)))
717 return -EFAULT;
718
719 /* Set restriction to the specified domain, or check it matches */
720 if (data->domid == DOMID_INVALID)
721 data->domid = dom;
722 else if (data->domid != dom)
723 return -EINVAL;
724
725 return 0;
726}
727
686static long privcmd_ioctl(struct file *file,728static long privcmd_ioctl(struct file *file,
687 unsigned int cmd, unsigned long data)729 unsigned int cmd, unsigned long data)
688{730{
@@ -691,23 +733,27 @@ static long privcmd_ioctl(struct file *file, · 2 unmodified lines
691 733
692 switch (cmd) {734 switch (cmd) {
693 case IOCTL_PRIVCMD_HYPERCALL:735 case IOCTL_PRIVCMD_HYPERCALL:
694 ret = privcmd_ioctl_hypercall(udata);736 ret = privcmd_ioctl_hypercall(file, udata);
695 break;737 break;
696 738
697 case IOCTL_PRIVCMD_MMAP:739 case IOCTL_PRIVCMD_MMAP:
698 ret = privcmd_ioctl_mmap(udata);740 ret = privcmd_ioctl_mmap(file, udata);
699 break;741 break;
700 742
701 case IOCTL_PRIVCMD_MMAPBATCH:743 case IOCTL_PRIVCMD_MMAPBATCH:
702 ret = privcmd_ioctl_mmap_batch(udata, 1);744 ret = privcmd_ioctl_mmap_batch(file, udata, 1);
703 break;745 break;
704 746
705 case IOCTL_PRIVCMD_MMAPBATCH_V2:747 case IOCTL_PRIVCMD_MMAPBATCH_V2:
706 ret = privcmd_ioctl_mmap_batch(udata, 2);748 ret = privcmd_ioctl_mmap_batch(file, udata, 2);
707 break;749 break;
708 750
709 case IOCTL_PRIVCMD_DM_OP:751 case IOCTL_PRIVCMD_DM_OP:
710 ret = privcmd_ioctl_dm_op(udata);752 ret = privcmd_ioctl_dm_op(file, udata);
753 break;
754
755 case IOCTL_PRIVCMD_RESTRICT:
756 ret = privcmd_ioctl_restrict(file, udata);
711 break;757 break;
712 758
713 default:759 default:
@@ -717,6 +763,28 @@ static long privcmd_ioctl(struct file *file, · 3 unmodified lines
717 return ret;763 return ret;
718}764}
719 765
766static int privcmd_open(struct inode *ino, struct file *file)
767{
768 struct privcmd_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
769
770 if (!data)
771 return -ENOMEM;
772
773 /* DOMID_INVALID implies no restriction */
774 data->domid = DOMID_INVALID;
775
776 file->private_data = data;
777 return 0;
778}
779
780static int privcmd_release(struct inode *ino, struct file *file)
781{
782 struct privcmd_data *data = file->private_data;
783
784 kfree(data);
785 return 0;
786}
787
720static void privcmd_close(struct vm_area_struct *vma)788static void privcmd_close(struct vm_area_struct *vma)
721{789{
722 struct page **pages = vma->vm_private_data;790 struct page **pages = vma->vm_private_data;
@@ -785,6 +853,8 @@ static int privcmd_vma_range_is_mapped( · 62 unmodified lines
785const struct file_operations xen_privcmd_fops = {853const struct file_operations xen_privcmd_fops = {
786 .owner = THIS_MODULE,854 .owner = THIS_MODULE,
787 .unlocked_ioctl = privcmd_ioctl,855 .unlocked_ioctl = privcmd_ioctl,
856 .open = privcmd_open,
857 .release = privcmd_release,
788 .mmap = privcmd_mmap,858 .mmap = privcmd_mmap,
789};859};
790EXPORT_SYMBOL_GPL(xen_privcmd_fops);860EXPORT_SYMBOL_GPL(xen_privcmd_fops);
include/uapi/xen/privcmd.h+2−0
@@ -111,5 +111,7 @@ struct privcmd_dm_op { · 110 unmodified lines
111 _IOC(_IOC_NONE, 'P', 4, sizeof(struct privcmd_mmapbatch_v2))111 _IOC(_IOC_NONE, 'P', 4, sizeof(struct privcmd_mmapbatch_v2))
112#define IOCTL_PRIVCMD_DM_OP \112#define IOCTL_PRIVCMD_DM_OP \
113 _IOC(_IOC_NONE, 'P', 5, sizeof(struct privcmd_dm_op))113 _IOC(_IOC_NONE, 'P', 5, sizeof(struct privcmd_dm_op))
114#define IOCTL_PRIVCMD_RESTRICT \
115 _IOC(_IOC_NONE, 'P', 6, sizeof(domid_t))
114 116
115#endif /* __LINUX_PUBLIC_PRIVCMD_H__ */117#endif /* __LINUX_PUBLIC_PRIVCMD_H__ */