| From foo@baz Mon Dec 20 02:55:54 PM CET 2021 |
| From: Juergen Gross <jgross@suse.com> |
| Date: Thu, 16 Dec 2021 08:24:08 +0100 |
| Subject: xen/console: harden hvc_xen against event channel storms |
| |
| From: Juergen Gross <jgross@suse.com> |
| |
| commit fe415186b43df0db1f17fa3a46275fd92107fe71 upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/tty/hvc/hvc_xen.c | 30 +++++++++++++++++++++++++++--- |
| drivers/xen/events/events_base.c | 6 ++++++ |
| include/xen/events.h | 1 + |
| 3 files changed, 34 insertions(+), 3 deletions(-) |
| |
| --- a/drivers/tty/hvc/hvc_xen.c |
| +++ b/drivers/tty/hvc/hvc_xen.c |
| @@ -37,6 +37,8 @@ struct xencons_info { |
| struct xenbus_device *xbdev; |
| struct xencons_interface *intf; |
| unsigned int evtchn; |
| + XENCONS_RING_IDX out_cons; |
| + unsigned int out_cons_same; |
| struct hvc_struct *hvc; |
| int irq; |
| int vtermno; |
| @@ -138,6 +140,8 @@ static int domU_read_console(uint32_t vt |
| XENCONS_RING_IDX cons, prod; |
| int recv = 0; |
| struct xencons_info *xencons = vtermno_to_xencons(vtermno); |
| + unsigned int eoiflag = 0; |
| + |
| if (xencons == NULL) |
| return -EINVAL; |
| intf = xencons->intf; |
| @@ -157,7 +161,27 @@ static int domU_read_console(uint32_t vt |
| mb(); /* read ring before consuming */ |
| 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; |
| } |
| |
| @@ -386,7 +410,7 @@ static int xencons_connect_backend(struc |
| if (ret) |
| return ret; |
| info->evtchn = evtchn; |
| - irq = bind_evtchn_to_irq(evtchn); |
| + irq = bind_interdomain_evtchn_to_irq_lateeoi(dev, evtchn); |
| if (irq < 0) |
| return irq; |
| info->irq = irq; |
| @@ -550,7 +574,7 @@ static int __init xen_hvc_init(void) |
| return r; |
| |
| 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) |
| info->irq = 0; /* NO_IRQ */ |
| --- a/drivers/xen/events/events_base.c |
| +++ b/drivers/xen/events/events_base.c |
| @@ -1251,6 +1251,12 @@ int bind_evtchn_to_irq(evtchn_port_t evt |
| } |
| 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) |
| { |
| struct evtchn_bind_ipi bind_ipi; |
| --- a/include/xen/events.h |
| +++ b/include/xen/events.h |
| @@ -17,6 +17,7 @@ struct xenbus_device; |
| unsigned xen_evtchn_nr_channels(void); |
| |
| 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, |
| irq_handler_t handler, |
| unsigned long irqflags, const char *devname, |