mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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:
1 parent
fdac951560
commit
545607eb3c
9 files changed
+93
-30
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, {})
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__ */
|
||||