evtchn: check control block exists when using FIFO-based events

When using the FIFO-based event channels, there are no checks for the
existance of a control block when binding an event or moving it to a
different VCPU.  This is because events may be bound when the ABI is
in 2-level mode (e.g., by the toolstack before the domain is started).

The guest may trigger a Xen crash in evtchn_fifo_set_pending() if:

  a) the event is bound to a VCPU without a control block; or
  b) VCPU 0 does not have a control block.

In case (a), Xen will crash when looking up the current queue.  In
(b), Xen will crash when looking up the old queue (which defaults to a
queue on VCPU 0).

By allocating all the per-VCPU structures when enabling the FIFO ABI,
we can be sure that v->evtchn_fifo is always valid.

EVTCHNOP_init_control_block for all the other CPUs need only map the
shared control block.

A single check in evtchn_fifo_set_pending() before accessing the
control block fixes all cases where the guest has not initialized some
control blocks.

This is XSA-107.

Reported-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
David Vrabel authored and Jan Beulich committed 2014-09-09 15:25:58 +02:00
1 parent 0561e1f01e
commit a4e0cea6fc
1 file changed
+58 -25
+58 -25
View File
@@ -177,6 +177,18 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn)
event_word_t *tail_word;
bool_t linked = 0;
/*
* Control block not mapped. The guest must not unmask an
* event until the control block is initialized, so we can
* just drop the event.
*/
if ( unlikely(!v->evtchn_fifo->control_block) )
{
printk(XENLOG_G_WARNING
"%pv has no FIFO event channel control block\n", v);
goto done;
}
/*
* No locking around getting the queue. This may race with
* changing the priority but we are allowed to signal the
@@ -385,36 +397,42 @@ static void init_queue(struct vcpu *v, struct evtchn_fifo_queue *q,
{
spin_lock_init(&q->lock);
q->priority = i;
q->head = &v->evtchn_fifo->control_block->head[i];
}
static int setup_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset)
static int setup_control_block(struct vcpu *v)
{
struct domain *d = v->domain;
struct evtchn_fifo_vcpu *efv;
void *virt;
unsigned int i;
int rc;
if ( v->evtchn_fifo )
return -EINVAL;
efv = xzalloc(struct evtchn_fifo_vcpu);
if ( !efv )
return -ENOMEM;
rc = map_guest_page(d, gfn, &virt);
if ( rc < 0 )
{
xfree(efv);
return rc;
}
for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ )
init_queue(v, &efv->queue[i], i);
v->evtchn_fifo = efv;
return 0;
}
static int map_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset)
{
void *virt;
unsigned int i;
int rc;
if ( v->evtchn_fifo->control_block )
return -EINVAL;
rc = map_guest_page(v->domain, gfn, &virt);
if ( rc < 0 )
return rc;
v->evtchn_fifo->control_block = virt + offset;
for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ )
init_queue(v, &v->evtchn_fifo->queue[i], i);
v->evtchn_fifo->queue[i].head = &v->evtchn_fifo->control_block->head[i];
return 0;
}
@@ -509,28 +527,43 @@ int evtchn_fifo_init_control(struct evtchn_init_control *init_control)
spin_lock(&d->event_lock);
rc = setup_control_block(v, gfn, offset);
/*
* If this is the first control block, setup an empty event array
* and switch to the fifo port ops.
*/
if ( rc == 0 && !d->evtchn_fifo )
if ( !d->evtchn_fifo )
{
struct vcpu *vcb;
for_each_vcpu ( d, vcb ) {
rc = setup_control_block(vcb);
if ( rc < 0 )
goto error;
}
rc = setup_event_array(d);
if ( rc < 0 )
cleanup_control_block(v);
else
{
d->evtchn_port_ops = &evtchn_port_ops_fifo;
d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS;
setup_ports(d);
}
goto error;
rc = map_control_block(v, gfn, offset);
if ( rc < 0 )
goto error;
d->evtchn_port_ops = &evtchn_port_ops_fifo;
d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS;
setup_ports(d);
}
else
rc = map_control_block(v, gfn, offset);
spin_unlock(&d->event_lock);
return rc;
error:
evtchn_fifo_destroy(d);
spin_unlock(&d->event_lock);
return rc;
}
static int add_page_to_event_array(struct domain *d, unsigned long gfn)
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.