Skip to content

Commit

Permalink
xen/evtchn: add IOCTL_EVTCHN_RESTRICT
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Vrabel committed Jul 25, 2016
1 parent aea305e commit fbc872c
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 0 deletions.
40 changes: 40 additions & 0 deletions drivers/xen/evtchn.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,12 @@ struct per_user_data {
wait_queue_head_t evtchn_wait;
struct fasync_struct *evtchn_async_queue;
const char *name;

domid_t restrict_domid;
};

#define UNRESTRICTED_DOMID ((domid_t)-1)

struct user_evtchn {
struct rb_node node;
struct per_user_data *user;
Expand Down Expand Up @@ -443,6 +447,10 @@ static long evtchn_ioctl(struct file *file,
struct ioctl_evtchn_bind_virq bind;
struct evtchn_bind_virq bind_virq;

rc = -EACCES;
if (u->restrict_domid != UNRESTRICTED_DOMID)
break;

rc = -EFAULT;
if (copy_from_user(&bind, uarg, sizeof(bind)))
break;
Expand All @@ -468,6 +476,11 @@ static long evtchn_ioctl(struct file *file,
if (copy_from_user(&bind, uarg, sizeof(bind)))
break;

rc = -EACCES;
if (u->restrict_domid != UNRESTRICTED_DOMID &&
u->restrict_domid != bind.remote_domain)
break;

bind_interdomain.remote_dom = bind.remote_domain;
bind_interdomain.remote_port = bind.remote_port;
rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
Expand All @@ -485,6 +498,10 @@ static long evtchn_ioctl(struct file *file,
struct ioctl_evtchn_bind_unbound_port bind;
struct evtchn_alloc_unbound alloc_unbound;

rc = -EACCES;
if (u->restrict_domid != UNRESTRICTED_DOMID)
break;

rc = -EFAULT;
if (copy_from_user(&bind, uarg, sizeof(bind)))
break;
Expand Down Expand Up @@ -553,6 +570,27 @@ static long evtchn_ioctl(struct file *file,
break;
}

case IOCTL_EVTCHN_RESTRICT_DOMID: {
struct ioctl_evtchn_restrict_domid ierd;

rc = -EACCES;
if (u->restrict_domid != UNRESTRICTED_DOMID)
break;

rc = -EFAULT;
if (copy_from_user(&ierd, uarg, sizeof(ierd)))
break;

rc = -EINVAL;
if (ierd.domid == 0 || ierd.domid >= DOMID_FIRST_RESERVED)
break;

u->restrict_domid = ierd.domid;
rc = 0;

break;
}

default:
rc = -ENOSYS;
break;
Expand Down Expand Up @@ -601,6 +639,8 @@ static int evtchn_open(struct inode *inode, struct file *filp)
mutex_init(&u->ring_cons_mutex);
spin_lock_init(&u->ring_prod_lock);

u->restrict_domid = UNRESTRICTED_DOMID;

filp->private_data = u;

return nonseekable_open(inode, filp);
Expand Down
15 changes: 15 additions & 0 deletions include/uapi/xen/evtchn.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,19 @@ struct ioctl_evtchn_notify {
#define IOCTL_EVTCHN_RESET \
_IOC(_IOC_NONE, 'E', 5, 0)

/*
* Restrict this file descriptor so that it can only be used to bind
* new interdomain events from one domain.
*
* Once a file descriptor has been restricted it cannot be
* de-restricted, and must be closed and re-opened. Event channels
* which were bound before restricting remain bound afterwards, and
* can be notified as usual.
*/
#define IOCTL_EVTCHN_RESTRICT_DOMID \
_IOC(_IOC_NONE, 'E', 6, sizeof(struct ioctl_evtchn_restrict_domid))
struct ioctl_evtchn_restrict_domid {
domid_t domid;
};

#endif /* __LINUX_PUBLIC_EVTCHN_H__ */

0 comments on commit fbc872c

Please sign in to comment.