diff options
-rw-r--r-- | drivers/xen/privcmd.c | 88 | ||||
-rw-r--r-- | include/uapi/xen/privcmd.h | 2 |
2 files changed, 81 insertions, 9 deletions
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 1a6f1860e008..2077a3ac7c0c 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c | |||
@@ -56,16 +56,25 @@ module_param_named(dm_op_buf_max_size, privcmd_dm_op_buf_max_size, uint, | |||
56 | MODULE_PARM_DESC(dm_op_buf_max_size, | 56 | MODULE_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 | ||
59 | struct privcmd_data { | ||
60 | domid_t domid; | ||
61 | }; | ||
62 | |||
59 | static int privcmd_vma_range_is_mapped( | 63 | static 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 | ||
64 | static long privcmd_ioctl_hypercall(void __user *udata) | 68 | static 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) | |||
242 | return 0; | 251 | return 0; |
243 | } | 252 | } |
244 | 253 | ||
245 | static long privcmd_ioctl_mmap(void __user *udata) | 254 | static 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) | |||
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) | |||
429 | 443 | ||
430 | static const struct vm_operations_struct privcmd_vm_ops; | 444 | static const struct vm_operations_struct privcmd_vm_ops; |
431 | 445 | ||
432 | static long privcmd_ioctl_mmap_batch(void __user *udata, int version) | 446 | static 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) | |||
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) | |||
603 | } | 623 | } |
604 | } | 624 | } |
605 | 625 | ||
606 | static long privcmd_ioctl_dm_op(void __user *udata) | 626 | static 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) | |||
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: | |||
683 | return rc; | 708 | return rc; |
684 | } | 709 | } |
685 | 710 | ||
711 | static 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 | |||
686 | static long privcmd_ioctl(struct file *file, | 728 | static 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, | |||
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, | |||
717 | return ret; | 763 | return ret; |
718 | } | 764 | } |
719 | 765 | ||
766 | static 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 | |||
780 | static 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 | |||
720 | static void privcmd_close(struct vm_area_struct *vma) | 788 | static 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( | |||
785 | const struct file_operations xen_privcmd_fops = { | 853 | const 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 | }; |
790 | EXPORT_SYMBOL_GPL(xen_privcmd_fops); | 860 | EXPORT_SYMBOL_GPL(xen_privcmd_fops); |
diff --git a/include/uapi/xen/privcmd.h b/include/uapi/xen/privcmd.h index f8c5d75b99e1..63ee95c9dabb 100644 --- a/include/uapi/xen/privcmd.h +++ b/include/uapi/xen/privcmd.h | |||
@@ -111,5 +111,7 @@ struct privcmd_dm_op { | |||
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__ */ |