Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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)

+34 -3
+27 -3
drivers/tty/hvc/hvc_xen.c
··· 37 37 struct xenbus_device *xbdev; 38 38 struct xencons_interface *intf; 39 39 unsigned int evtchn; 40 + XENCONS_RING_IDX out_cons; 41 + unsigned int out_cons_same; 40 42 struct hvc_struct *hvc; 41 43 int irq; 42 44 int vtermno; ··· 140 138 XENCONS_RING_IDX cons, prod; 141 139 int recv = 0; 142 140 struct xencons_info *xencons = vtermno_to_xencons(vtermno); 141 + unsigned int eoiflag = 0; 142 + 143 143 if (xencons == NULL) 144 144 return -EINVAL; 145 145 intf = xencons->intf; ··· 161 157 mb(); /* read ring before consuming */ 162 158 intf->in_cons = cons; 163 159 164 - notify_daemon(xencons); 160 + /* 161 + * When to mark interrupt having been spurious: 162 + * - there was no new data to be read, and 163 + * - the backend did not consume some output bytes, and 164 + * - the previous round with no read data didn't see consumed bytes 165 + * (we might have a race with an interrupt being in flight while 166 + * updating xencons->out_cons, so account for that by allowing one 167 + * round without any visible reason) 168 + */ 169 + if (intf->out_cons != xencons->out_cons) { 170 + xencons->out_cons = intf->out_cons; 171 + xencons->out_cons_same = 0; 172 + } 173 + if (recv) { 174 + notify_daemon(xencons); 175 + } else if (xencons->out_cons_same++ > 1) { 176 + eoiflag = XEN_EOI_FLAG_SPURIOUS; 177 + } 178 + 179 + xen_irq_lateeoi(xencons->irq, eoiflag); 180 + 165 181 return recv; 166 182 } 167 183 ··· 410 386 if (ret) 411 387 return ret; 412 388 info->evtchn = evtchn; 413 - irq = bind_evtchn_to_irq(evtchn); 389 + irq = bind_interdomain_evtchn_to_irq_lateeoi(dev, evtchn); 414 390 if (irq < 0) 415 391 return irq; 416 392 info->irq = irq; ··· 575 551 return r; 576 552 577 553 info = vtermno_to_xencons(HVC_COOKIE); 578 - info->irq = bind_evtchn_to_irq(info->evtchn); 554 + info->irq = bind_evtchn_to_irq_lateeoi(info->evtchn); 579 555 } 580 556 if (info->irq < 0) 581 557 info->irq = 0; /* NO_IRQ */
+6
drivers/xen/events/events_base.c
··· 1251 1251 } 1252 1252 EXPORT_SYMBOL_GPL(bind_evtchn_to_irq); 1253 1253 1254 + int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn) 1255 + { 1256 + return bind_evtchn_to_irq_chip(evtchn, &xen_lateeoi_chip, NULL); 1257 + } 1258 + EXPORT_SYMBOL_GPL(bind_evtchn_to_irq_lateeoi); 1259 + 1254 1260 static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) 1255 1261 { 1256 1262 struct evtchn_bind_ipi bind_ipi;
+1
include/xen/events.h
··· 17 17 unsigned xen_evtchn_nr_channels(void); 18 18 19 19 int bind_evtchn_to_irq(evtchn_port_t evtchn); 20 + int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn); 20 21 int bind_evtchn_to_irqhandler(evtchn_port_t evtchn, 21 22 irq_handler_t handler, 22 23 unsigned long irqflags, const char *devname,