x86: fix various issues with handling guest IRQs

- properly revoke IRQ access in map_domain_pirq() error path
- don't permit replacing an in use IRQ
- don't accept inputs in the GSI range for MAP_PIRQ_TYPE_MSI
- track IRQ access permission in host IRQ terms, not guest IRQ ones
  (and with that, also disallow Dom0 access to IRQ0)

This is CVE-2013-1919 / XSA-46.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
This commit is contained in:
Jan Beulich committed 2013-04-18 16:11:23 +02:00
1 parent fdac951560
commit 545607eb3c
9 files changed
+93 -30

No files matched your search

+7 -5
View File
@@ -966,14 +966,16 @@ static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
}
for (i = 0; i < d_config->b_info.num_irqs; i++) {
uint32_t irq = d_config->b_info.irqs[i];
int irq = d_config->b_info.irqs[i];
LOG(DEBUG, "dom%d irq %"PRIx32, domid, irq);
LOG(DEBUG, "dom%d irq %d", domid, irq);
ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
ret = irq >= 0 ? xc_physdev_map_pirq(CTX->xch, domid, irq, &irq)
: -EOVERFLOW;
if (!ret)
ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
if (ret < 0) {
LOGE(ERROR,
"failed give dom%d access to irq %"PRId32, domid, irq);
LOGE(ERROR, "failed give dom%d access to irq %d", domid, irq);
ret = ERROR_FAIL;
}
}
+6 -6
View File
@@ -73,6 +73,12 @@ class IRQController(DevController):
pirq = get_param('irq')
rc = xc.physdev_map_pirq(domid = self.getDomid(),
index = pirq,
pirq = pirq)
if rc < 0:
raise VmError('irq: Failed to map irq %x' % (pirq))
rc = xc.domain_irq_permission(domid = self.getDomid(),
pirq = pirq,
allow_access = True)
@@ -81,12 +87,6 @@ class IRQController(DevController):
#todo non-fatal
raise VmError(
'irq: Failed to configure irq: %d' % (pirq))
rc = xc.physdev_map_pirq(domid = self.getDomid(),
index = pirq,
pirq = pirq)
if rc < 0:
raise VmError(
'irq: Failed to map irq %x' % (pirq))
back = dict([(k, config[k]) for k in self.valid_cfg if k in config])
return (self.allocateDeviceID(), back, {})
+1 -1
View File
@@ -1080,7 +1080,7 @@ int __init construct_dom0(
/* DOM0 is permitted full I/O capabilities. */
rc |= ioports_permit_access(dom0, 0, 0xFFFF);
rc |= iomem_permit_access(dom0, 0UL, ~0UL);
rc |= irqs_permit_access(dom0, 0, d->nr_pirqs - 1);
rc |= irqs_permit_access(dom0, 1, nr_irqs_gsi - 1);
/*
* Modify I/O port access permissions.
+14 -6
View File
@@ -578,9 +578,13 @@ long arch_do_domctl(
break;
ret = -EPERM;
if ( !IS_PRIV(current->domain) &&
!irq_access_permitted(current->domain, bind->machine_irq) )
break;
if ( !IS_PRIV(current->domain) )
{
int irq = domain_pirq_to_irq(d, bind->machine_irq);
if ( irq <= 0 || !irq_access_permitted(current->domain, irq) )
break;
}
ret = -ESRCH;
if ( iommu_enabled )
@@ -602,9 +606,13 @@ long arch_do_domctl(
bind = &(domctl->u.bind_pt_irq);
ret = -EPERM;
if ( !IS_PRIV(current->domain) &&
!irq_access_permitted(current->domain, bind->machine_irq) )
break;
if ( !IS_PRIV(current->domain) )
{
int irq = domain_pirq_to_irq(d, bind->machine_irq);
if ( irq <= 0 || !irq_access_permitted(current->domain, irq) )
break;
}
ret = xsm_unbind_pt_irq(XSM_HOOK, d, bind);
if ( ret )
+42 -8
View File
@@ -185,6 +185,14 @@ int create_irq(int node)
desc->arch.used = IRQ_UNUSED;
irq = ret;
}
else if ( dom0 )
{
ret = irq_permit_access(dom0, irq);
if ( ret )
printk(XENLOG_G_ERR
"Could not grant Dom0 access to IRQ%d (error %d)\n",
irq, ret);
}
return irq;
}
@@ -281,6 +289,17 @@ void clear_irq_vector(int irq)
void destroy_irq(unsigned int irq)
{
BUG_ON(!MSI_IRQ(irq));
if ( dom0 )
{
int err = irq_deny_access(dom0, irq);
if ( err )
printk(XENLOG_G_ERR
"Could not revoke Dom0 access to IRQ%u (error %d)\n",
irq, err);
}
dynamic_irq_cleanup(irq);
clear_irq_vector(irq);
}
@@ -1873,7 +1892,7 @@ int map_domain_pirq(
ASSERT(spin_is_locked(&d->event_lock));
if ( !IS_PRIV(current->domain) &&
!irq_access_permitted(current->domain, pirq))
!irq_access_permitted(current->domain, irq))
return -EPERM;
if ( pirq < 0 || pirq >= d->nr_pirqs || irq < 0 || irq >= nr_irqs )
@@ -1902,17 +1921,18 @@ int map_domain_pirq(
return ret;
}
ret = irq_permit_access(d, pirq);
ret = irq_permit_access(d, irq);
if ( ret )
{
dprintk(XENLOG_G_ERR, "dom%d: could not permit access to irq %d\n",
d->domain_id, pirq);
printk(XENLOG_G_ERR
"dom%d: could not permit access to IRQ%d (pirq %d)\n",
d->domain_id, irq, pirq);
return ret;
}
ret = prepare_domain_irq_pirq(d, irq, pirq, &info);
if ( ret )
return ret;
goto revoke;
desc = irq_to_desc(irq);
@@ -1936,8 +1956,14 @@ int map_domain_pirq(
spin_lock_irqsave(&desc->lock, flags);
if ( desc->handler != &no_irq_type )
{
spin_unlock_irqrestore(&desc->lock, flags);
dprintk(XENLOG_G_ERR, "dom%d: irq %d in use\n",
d->domain_id, irq);
pci_disable_msi(msi_desc);
ret = -EBUSY;
goto done;
}
ret = setup_msi_irq(desc, msi_desc);
if ( ret )
@@ -1972,7 +1998,14 @@ int map_domain_pirq(
done:
if ( ret )
{
cleanup_domain_irq_pirq(d, irq, info);
revoke:
if ( irq_deny_access(d, irq) )
printk(XENLOG_G_ERR
"dom%d: could not revoke access to IRQ%d (pirq %d)\n",
d->domain_id, irq, pirq);
}
return ret;
}
@@ -2043,10 +2076,11 @@ int unmap_domain_pirq(struct domain *d, int pirq)
if ( !forced_unbind )
cleanup_domain_irq_pirq(d, irq, info);
ret = irq_deny_access(d, pirq);
ret = irq_deny_access(d, irq);
if ( ret )
dprintk(XENLOG_G_ERR, "dom%d: could not deny access to irq %d\n",
d->domain_id, pirq);
printk(XENLOG_G_ERR
"dom%d: could not deny access to IRQ%d (pirq %d)\n",
d->domain_id, irq, pirq);
done:
return ret;
+1 -1
View File
@@ -144,7 +144,7 @@ int physdev_map_pirq(domid_t domid, int type, int *index, int *pirq_p,
if ( irq == -1 )
irq = create_irq(NUMA_NO_NODE);
if ( irq < 0 || irq >= nr_irqs )
if ( irq < nr_irqs_gsi || irq >= nr_irqs )
{
dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
d->domain_id);
+3 -2
View File
@@ -25,6 +25,7 @@
#include <xen/paging.h>
#include <xen/hypercall.h>
#include <asm/current.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <public/domctl.h>
#include <xsm/xsm.h>
@@ -777,9 +778,9 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
else if ( xsm_irq_permission(XSM_HOOK, d, pirq, allow) )
ret = -EPERM;
else if ( allow )
ret = irq_permit_access(d, pirq);
ret = pirq_permit_access(d, pirq);
else
ret = irq_deny_access(d, pirq);
ret = pirq_deny_access(d, pirq);
}
break;
+1 -1
View File
@@ -369,7 +369,7 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
if ( (pirq < 0) || (pirq >= d->nr_pirqs) )
return -EINVAL;
if ( !is_hvm_domain(d) && !irq_access_permitted(d, pirq) )
if ( !is_hvm_domain(d) && !pirq_access_permitted(d, pirq) )
return -EPERM;
spin_lock(&d->event_lock);
+18
View File
@@ -28,4 +28,22 @@
#define irq_access_permitted(d, i) \
rangeset_contains_singleton((d)->irq_caps, i)
#define pirq_permit_access(d, i) ({ \
struct domain *d__ = (d); \
int i__ = domain_pirq_to_irq(d__, i); \
i__ > 0 ? rangeset_add_singleton(d__->irq_caps, i__)\
: -EINVAL; \
})
#define pirq_deny_access(d, i) ({ \
struct domain *d__ = (d); \
int i__ = domain_pirq_to_irq(d__, i); \
i__ > 0 ? rangeset_remove_singleton(d__->irq_caps, i__)\
: -EINVAL; \
})
#define pirq_access_permitted(d, i) ({ \
struct domain *d__ = (d); \
rangeset_contains_singleton(d__->irq_caps, \
domain_pirq_to_irq(d__, i));\
})
#endif /* __XEN_IOCAP_H__ */
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.