xen/console: harden hvc_xen against event channel storms
The Xen console driver is still vulnerable for an attack via excessive number of events sent by the backend. Fix that by using a lateeoi event channel. For the normal domU initial console this requires the introduction of bind_evtchn_to_irq_lateeoi() as there is no xenbus device available at the time the event channel is bound to the irq. As the decision whether an interrupt was spurious or not requires to test for bytes having been read from the backend, move sending the event into the if statement, as sending an event without having found any bytes to be read is making no sense at all. This is part of XSA-391 Signed-off-by: Juergen Gross <jgross@suse.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> --- V2: - slightly adapt spurious irq detection (Jan Beulich) V3: - fix spurious irq detection (Jan Beulich)
This commit is contained in:
parent
b27d47950e
commit
fe415186b4
|
@ -37,6 +37,8 @@ struct xencons_info {
|
||||||
struct xenbus_device *xbdev;
|
struct xenbus_device *xbdev;
|
||||||
struct xencons_interface *intf;
|
struct xencons_interface *intf;
|
||||||
unsigned int evtchn;
|
unsigned int evtchn;
|
||||||
|
XENCONS_RING_IDX out_cons;
|
||||||
|
unsigned int out_cons_same;
|
||||||
struct hvc_struct *hvc;
|
struct hvc_struct *hvc;
|
||||||
int irq;
|
int irq;
|
||||||
int vtermno;
|
int vtermno;
|
||||||
|
@ -138,6 +140,8 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
|
||||||
XENCONS_RING_IDX cons, prod;
|
XENCONS_RING_IDX cons, prod;
|
||||||
int recv = 0;
|
int recv = 0;
|
||||||
struct xencons_info *xencons = vtermno_to_xencons(vtermno);
|
struct xencons_info *xencons = vtermno_to_xencons(vtermno);
|
||||||
|
unsigned int eoiflag = 0;
|
||||||
|
|
||||||
if (xencons == NULL)
|
if (xencons == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
intf = xencons->intf;
|
intf = xencons->intf;
|
||||||
|
@ -157,7 +161,27 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
|
||||||
mb(); /* read ring before consuming */
|
mb(); /* read ring before consuming */
|
||||||
intf->in_cons = cons;
|
intf->in_cons = cons;
|
||||||
|
|
||||||
notify_daemon(xencons);
|
/*
|
||||||
|
* When to mark interrupt having been spurious:
|
||||||
|
* - there was no new data to be read, and
|
||||||
|
* - the backend did not consume some output bytes, and
|
||||||
|
* - the previous round with no read data didn't see consumed bytes
|
||||||
|
* (we might have a race with an interrupt being in flight while
|
||||||
|
* updating xencons->out_cons, so account for that by allowing one
|
||||||
|
* round without any visible reason)
|
||||||
|
*/
|
||||||
|
if (intf->out_cons != xencons->out_cons) {
|
||||||
|
xencons->out_cons = intf->out_cons;
|
||||||
|
xencons->out_cons_same = 0;
|
||||||
|
}
|
||||||
|
if (recv) {
|
||||||
|
notify_daemon(xencons);
|
||||||
|
} else if (xencons->out_cons_same++ > 1) {
|
||||||
|
eoiflag = XEN_EOI_FLAG_SPURIOUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
xen_irq_lateeoi(xencons->irq, eoiflag);
|
||||||
|
|
||||||
return recv;
|
return recv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +410,7 @@ static int xencons_connect_backend(struct xenbus_device *dev,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
info->evtchn = evtchn;
|
info->evtchn = evtchn;
|
||||||
irq = bind_evtchn_to_irq(evtchn);
|
irq = bind_interdomain_evtchn_to_irq_lateeoi(dev, evtchn);
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
return irq;
|
return irq;
|
||||||
info->irq = irq;
|
info->irq = irq;
|
||||||
|
@ -551,7 +575,7 @@ static int __init xen_hvc_init(void)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
info = vtermno_to_xencons(HVC_COOKIE);
|
info = vtermno_to_xencons(HVC_COOKIE);
|
||||||
info->irq = bind_evtchn_to_irq(info->evtchn);
|
info->irq = bind_evtchn_to_irq_lateeoi(info->evtchn);
|
||||||
}
|
}
|
||||||
if (info->irq < 0)
|
if (info->irq < 0)
|
||||||
info->irq = 0; /* NO_IRQ */
|
info->irq = 0; /* NO_IRQ */
|
||||||
|
|
|
@ -1251,6 +1251,12 @@ int bind_evtchn_to_irq(evtchn_port_t evtchn)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bind_evtchn_to_irq);
|
EXPORT_SYMBOL_GPL(bind_evtchn_to_irq);
|
||||||
|
|
||||||
|
int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn)
|
||||||
|
{
|
||||||
|
return bind_evtchn_to_irq_chip(evtchn, &xen_lateeoi_chip, NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(bind_evtchn_to_irq_lateeoi);
|
||||||
|
|
||||||
static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
|
static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct evtchn_bind_ipi bind_ipi;
|
struct evtchn_bind_ipi bind_ipi;
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct xenbus_device;
|
||||||
unsigned xen_evtchn_nr_channels(void);
|
unsigned xen_evtchn_nr_channels(void);
|
||||||
|
|
||||||
int bind_evtchn_to_irq(evtchn_port_t evtchn);
|
int bind_evtchn_to_irq(evtchn_port_t evtchn);
|
||||||
|
int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn);
|
||||||
int bind_evtchn_to_irqhandler(evtchn_port_t evtchn,
|
int bind_evtchn_to_irqhandler(evtchn_port_t evtchn,
|
||||||
irq_handler_t handler,
|
irq_handler_t handler,
|
||||||
unsigned long irqflags, const char *devname,
|
unsigned long irqflags, const char *devname,
|
||||||
|
|
Loading…
Reference in New Issue