diff options
author | Jan200101 <sentrycraft123@gmail.com> | 2020-11-03 17:22:33 +0100 |
---|---|---|
committer | Jan200101 <sentrycraft123@gmail.com> | 2020-12-05 19:40:07 +0100 |
commit | 341c2b7474ae9534963c9001cae5b8e6ecefe0f2 (patch) | |
tree | de02864eb15585e56915d9e9c987c3675fa4f4ff /SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch | |
parent | 7fa28673e01cd07016a83be8169a0bd46ba63a04 (diff) | |
download | kernel-fsync-341c2b7474ae9534963c9001cae5b8e6ecefe0f2.tar.gz kernel-fsync-341c2b7474ae9534963c9001cae5b8e6ecefe0f2.zip |
kernel 5.8.17
Diffstat (limited to 'SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch')
-rw-r--r-- | SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch b/SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch new file mode 100644 index 0000000..cd4ac3d --- /dev/null +++ b/SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch @@ -0,0 +1,161 @@ +From 073d0552ead5bfc7a3a9c01de590e924f11b5dd2 Mon Sep 17 00:00:00 2001 +From: Juergen Gross <jgross@suse.com> +Date: Mon, 7 Sep 2020 15:47:27 +0200 +Subject: [PATCH] xen/events: avoid removing an event channel while handling it +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Today it can happen that an event channel is being removed from the +system while the event handling loop is active. This can lead to a +race resulting in crashes or WARN() splats when trying to access the +irq_info structure related to the event channel. + +Fix this problem by using a rwlock taken as reader in the event +handling loop and as writer when deallocating the irq_info structure. + +As the observed problem was a NULL dereference in evtchn_from_irq() +make this function more robust against races by testing the irq_info +pointer to be not NULL before dereferencing it. + +And finally make all accesses to evtchn_to_irq[row][col] atomic ones +in order to avoid seeing partial updates of an array element in irq +handling. Note that irq handling can be entered only for event channels +which have been valid before, so any not populated row isn't a problem +in this regard, as rows are only ever added and never removed. + +This is XSA-331. + +Cc: stable@vger.kernel.org +Reported-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> +Reported-by: Jinoh Kang <luke1337@theori.io> +Signed-off-by: Juergen Gross <jgross@suse.com> +Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> +Reviewed-by: Wei Liu <wl@xen.org> +--- + drivers/xen/events/events_base.c | 41 ++++++++++++++++++++++++++++---- + 1 file changed, 36 insertions(+), 5 deletions(-) + +diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c +index 6f02c18fa65c..407741ece084 100644 +--- a/drivers/xen/events/events_base.c ++++ b/drivers/xen/events/events_base.c +@@ -33,6 +33,7 @@ + #include <linux/slab.h> + #include <linux/irqnr.h> + #include <linux/pci.h> ++#include <linux/spinlock.h> + + #ifdef CONFIG_X86 + #include <asm/desc.h> +@@ -71,6 +72,23 @@ const struct evtchn_ops *evtchn_ops; + */ + static DEFINE_MUTEX(irq_mapping_update_lock); + ++/* ++ * Lock protecting event handling loop against removing event channels. ++ * Adding of event channels is no issue as the associated IRQ becomes active ++ * only after everything is setup (before request_[threaded_]irq() the handler ++ * can't be entered for an event, as the event channel will be unmasked only ++ * then). ++ */ ++static DEFINE_RWLOCK(evtchn_rwlock); ++ ++/* ++ * Lock hierarchy: ++ * ++ * irq_mapping_update_lock ++ * evtchn_rwlock ++ * IRQ-desc lock ++ */ ++ + static LIST_HEAD(xen_irq_list_head); + + /* IRQ <-> VIRQ mapping. */ +@@ -105,7 +123,7 @@ static void clear_evtchn_to_irq_row(unsigned row) + unsigned col; + + for (col = 0; col < EVTCHN_PER_ROW; col++) +- evtchn_to_irq[row][col] = -1; ++ WRITE_ONCE(evtchn_to_irq[row][col], -1); + } + + static void clear_evtchn_to_irq_all(void) +@@ -142,7 +160,7 @@ static int set_evtchn_to_irq(evtchn_port_t evtchn, unsigned int irq) + clear_evtchn_to_irq_row(row); + } + +- evtchn_to_irq[row][col] = irq; ++ WRITE_ONCE(evtchn_to_irq[row][col], irq); + return 0; + } + +@@ -152,7 +170,7 @@ int get_evtchn_to_irq(evtchn_port_t evtchn) + return -1; + if (evtchn_to_irq[EVTCHN_ROW(evtchn)] == NULL) + return -1; +- return evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)]; ++ return READ_ONCE(evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)]); + } + + /* Get info for IRQ */ +@@ -261,10 +279,14 @@ static void xen_irq_info_cleanup(struct irq_info *info) + */ + evtchn_port_t evtchn_from_irq(unsigned irq) + { +- if (WARN(irq >= nr_irqs, "Invalid irq %d!\n", irq)) ++ const struct irq_info *info = NULL; ++ ++ if (likely(irq < nr_irqs)) ++ info = info_for_irq(irq); ++ if (!info) + return 0; + +- return info_for_irq(irq)->evtchn; ++ return info->evtchn; + } + + unsigned int irq_from_evtchn(evtchn_port_t evtchn) +@@ -440,16 +462,21 @@ static int __must_check xen_allocate_irq_gsi(unsigned gsi) + static void xen_free_irq(unsigned irq) + { + struct irq_info *info = info_for_irq(irq); ++ unsigned long flags; + + if (WARN_ON(!info)) + return; + ++ write_lock_irqsave(&evtchn_rwlock, flags); ++ + list_del(&info->list); + + set_info_for_irq(irq, NULL); + + WARN_ON(info->refcnt > 0); + ++ write_unlock_irqrestore(&evtchn_rwlock, flags); ++ + kfree(info); + + /* Legacy IRQ descriptors are managed by the arch. */ +@@ -1233,6 +1260,8 @@ static void __xen_evtchn_do_upcall(void) + struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu); + int cpu = smp_processor_id(); + ++ read_lock(&evtchn_rwlock); ++ + do { + vcpu_info->evtchn_upcall_pending = 0; + +@@ -1243,6 +1272,8 @@ static void __xen_evtchn_do_upcall(void) + virt_rmb(); /* Hypervisor can set upcall pending. */ + + } while (vcpu_info->evtchn_upcall_pending); ++ ++ read_unlock(&evtchn_rwlock); + } + + void xen_evtchn_do_upcall(struct pt_regs *regs) +-- +2.28.0 + |