Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xen/evtchn: add IOCTL_EVTCHN_RESTRICT

IOCTL_EVTCHN_RESTRICT limits the file descriptor to being able to bind
to interdomain event channels from a specific domain. Event channels
that are already bound continue to work for sending and receiving
notifications.

This is useful as part of deprivileging a user space PV backend or
device model (QEMU). e.g., Once the device model as bound to the
ioreq server event channels it can restrict the file handle so an
exploited DM cannot use it to create or bind to arbitrary event
channels.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>

+55
+40
drivers/xen/evtchn.c
··· 73 73 wait_queue_head_t evtchn_wait; 74 74 struct fasync_struct *evtchn_async_queue; 75 75 const char *name; 76 + 77 + domid_t restrict_domid; 76 78 }; 79 + 80 + #define UNRESTRICTED_DOMID ((domid_t)-1) 77 81 78 82 struct user_evtchn { 79 83 struct rb_node node; ··· 447 443 struct ioctl_evtchn_bind_virq bind; 448 444 struct evtchn_bind_virq bind_virq; 449 445 446 + rc = -EACCES; 447 + if (u->restrict_domid != UNRESTRICTED_DOMID) 448 + break; 449 + 450 450 rc = -EFAULT; 451 451 if (copy_from_user(&bind, uarg, sizeof(bind))) 452 452 break; ··· 476 468 if (copy_from_user(&bind, uarg, sizeof(bind))) 477 469 break; 478 470 471 + rc = -EACCES; 472 + if (u->restrict_domid != UNRESTRICTED_DOMID && 473 + u->restrict_domid != bind.remote_domain) 474 + break; 475 + 479 476 bind_interdomain.remote_dom = bind.remote_domain; 480 477 bind_interdomain.remote_port = bind.remote_port; 481 478 rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, ··· 497 484 case IOCTL_EVTCHN_BIND_UNBOUND_PORT: { 498 485 struct ioctl_evtchn_bind_unbound_port bind; 499 486 struct evtchn_alloc_unbound alloc_unbound; 487 + 488 + rc = -EACCES; 489 + if (u->restrict_domid != UNRESTRICTED_DOMID) 490 + break; 500 491 501 492 rc = -EFAULT; 502 493 if (copy_from_user(&bind, uarg, sizeof(bind))) ··· 570 553 break; 571 554 } 572 555 556 + case IOCTL_EVTCHN_RESTRICT_DOMID: { 557 + struct ioctl_evtchn_restrict_domid ierd; 558 + 559 + rc = -EACCES; 560 + if (u->restrict_domid != UNRESTRICTED_DOMID) 561 + break; 562 + 563 + rc = -EFAULT; 564 + if (copy_from_user(&ierd, uarg, sizeof(ierd))) 565 + break; 566 + 567 + rc = -EINVAL; 568 + if (ierd.domid == 0 || ierd.domid >= DOMID_FIRST_RESERVED) 569 + break; 570 + 571 + u->restrict_domid = ierd.domid; 572 + rc = 0; 573 + 574 + break; 575 + } 576 + 573 577 default: 574 578 rc = -ENOSYS; 575 579 break; ··· 638 600 mutex_init(&u->bind_mutex); 639 601 mutex_init(&u->ring_cons_mutex); 640 602 spin_lock_init(&u->ring_prod_lock); 603 + 604 + u->restrict_domid = UNRESTRICTED_DOMID; 641 605 642 606 filp->private_data = u; 643 607
+15
include/uapi/xen/evtchn.h
··· 85 85 #define IOCTL_EVTCHN_RESET \ 86 86 _IOC(_IOC_NONE, 'E', 5, 0) 87 87 88 + /* 89 + * Restrict this file descriptor so that it can only be used to bind 90 + * new interdomain events from one domain. 91 + * 92 + * Once a file descriptor has been restricted it cannot be 93 + * de-restricted, and must be closed and re-opened. Event channels 94 + * which were bound before restricting remain bound afterwards, and 95 + * can be notified as usual. 96 + */ 97 + #define IOCTL_EVTCHN_RESTRICT_DOMID \ 98 + _IOC(_IOC_NONE, 'E', 6, sizeof(struct ioctl_evtchn_restrict_domid)) 99 + struct ioctl_evtchn_restrict_domid { 100 + domid_t domid; 101 + }; 102 + 88 103 #endif /* __LINUX_PUBLIC_EVTCHN_H__ */