summaryrefslogtreecommitdiff
path: root/SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch
diff options
context:
space:
mode:
authorJan200101 <sentrycraft123@gmail.com>2020-11-03 17:22:33 +0100
committerJan200101 <sentrycraft123@gmail.com>2020-12-05 19:40:07 +0100
commit341c2b7474ae9534963c9001cae5b8e6ecefe0f2 (patch)
treede02864eb15585e56915d9e9c987c3675fa4f4ff /SOURCES/0001-xen-events-avoid-removing-an-event-channel-while-han.patch
parent7fa28673e01cd07016a83be8169a0bd46ba63a04 (diff)
downloadkernel-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.patch161
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
+