summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomáš Mózes <hydrapolic@gmail.com>2024-04-05 08:59:40 +0200
committerTomáš Mózes <hydrapolic@gmail.com>2024-04-05 08:59:40 +0200
commitd0ce95087288b30e5e211bac8e9a0817f2effcf5 (patch)
treece2e128cfdf8d491a494d6583979bc5330db21e2 /0030-xen-livepatch-register-livepatch-regions-when-loaded.patch
parentXen 4.17.4-pre-patchset-0 (diff)
downloadxen-upstream-patches-d0ce95087288b30e5e211bac8e9a0817f2effcf5.tar.gz
xen-upstream-patches-d0ce95087288b30e5e211bac8e9a0817f2effcf5.tar.bz2
xen-upstream-patches-d0ce95087288b30e5e211bac8e9a0817f2effcf5.zip
Xen 4.17.4-pre-patchset-14.17.4-pre-patchset-14.17
Signed-off-by: Tomáš Mózes <hydrapolic@gmail.com>
Diffstat (limited to '0030-xen-livepatch-register-livepatch-regions-when-loaded.patch')
-rw-r--r--0030-xen-livepatch-register-livepatch-regions-when-loaded.patch159
1 files changed, 159 insertions, 0 deletions
diff --git a/0030-xen-livepatch-register-livepatch-regions-when-loaded.patch b/0030-xen-livepatch-register-livepatch-regions-when-loaded.patch
new file mode 100644
index 0000000..f521ecc
--- /dev/null
+++ b/0030-xen-livepatch-register-livepatch-regions-when-loaded.patch
@@ -0,0 +1,159 @@
+From b11917de0cd261a878beaf50c18a689bde0b2f50 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= <roger.pau@citrix.com>
+Date: Tue, 5 Mar 2024 11:59:26 +0100
+Subject: [PATCH 30/67] xen/livepatch: register livepatch regions when loaded
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently livepatch regions are registered as virtual regions only after the
+livepatch has been applied.
+
+This can lead to issues when using the pre-apply or post-revert hooks, as at
+that point the livepatch is not in the virtual regions list. If a livepatch
+pre-apply hook contains a WARN() it would trigger an hypervisor crash, as the
+code to handle the bug frame won't be able to find the instruction pointer that
+triggered the #UD in any of the registered virtual regions, and hence crash.
+
+Fix this by adding the livepatch payloads as virtual regions as soon as loaded,
+and only remove them once the payload is unloaded. This requires some changes
+to the virtual regions code, as the removal of the virtual regions is no longer
+done in stop machine context, and hence an RCU barrier is added in order to
+make sure there are no users of the virtual region after it's been removed from
+the list.
+
+Fixes: 8313c864fa95 ('livepatch: Implement pre-|post- apply|revert hooks')
+Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
+Reviewed-by: Ross Lagerwall <ross.lagerwall@citrix.com>
+master commit: a57b4074ab39bee78b6c116277f0a9963bd8e687
+master date: 2024-02-28 16:57:25 +0000
+---
+ xen/common/livepatch.c | 4 ++--
+ xen/common/virtual_region.c | 44 ++++++++++++++-----------------------
+ 2 files changed, 19 insertions(+), 29 deletions(-)
+
+diff --git a/xen/common/livepatch.c b/xen/common/livepatch.c
+index c2ae84d18b..537e9f33e4 100644
+--- a/xen/common/livepatch.c
++++ b/xen/common/livepatch.c
+@@ -1015,6 +1015,7 @@ static int build_symbol_table(struct payload *payload,
+ static void free_payload(struct payload *data)
+ {
+ ASSERT(spin_is_locked(&payload_lock));
++ unregister_virtual_region(&data->region);
+ list_del(&data->list);
+ payload_cnt--;
+ payload_version++;
+@@ -1114,6 +1115,7 @@ static int livepatch_upload(struct xen_sysctl_livepatch_upload *upload)
+ INIT_LIST_HEAD(&data->list);
+ INIT_LIST_HEAD(&data->applied_list);
+
++ register_virtual_region(&data->region);
+ list_add_tail(&data->list, &payload_list);
+ payload_cnt++;
+ payload_version++;
+@@ -1330,7 +1332,6 @@ static inline void apply_payload_tail(struct payload *data)
+ * The applied_list is iterated by the trap code.
+ */
+ list_add_tail_rcu(&data->applied_list, &applied_list);
+- register_virtual_region(&data->region);
+
+ data->state = LIVEPATCH_STATE_APPLIED;
+ }
+@@ -1376,7 +1377,6 @@ static inline void revert_payload_tail(struct payload *data)
+ * The applied_list is iterated by the trap code.
+ */
+ list_del_rcu(&data->applied_list);
+- unregister_virtual_region(&data->region);
+
+ data->reverted = true;
+ data->state = LIVEPATCH_STATE_CHECKED;
+diff --git a/xen/common/virtual_region.c b/xen/common/virtual_region.c
+index 5f89703f51..9f12c30efe 100644
+--- a/xen/common/virtual_region.c
++++ b/xen/common/virtual_region.c
+@@ -23,14 +23,8 @@ static struct virtual_region core_init __initdata = {
+ };
+
+ /*
+- * RCU locking. Additions are done either at startup (when there is only
+- * one CPU) or when all CPUs are running without IRQs.
+- *
+- * Deletions are bit tricky. We do it when Live Patch (all CPUs running
+- * without IRQs) or during bootup (when clearing the init).
+- *
+- * Hence we use list_del_rcu (which sports an memory fence) and a spinlock
+- * on deletion.
++ * RCU locking. Modifications to the list must be done in exclusive mode, and
++ * hence need to hold the spinlock.
+ *
+ * All readers of virtual_region_list MUST use list_for_each_entry_rcu.
+ */
+@@ -58,41 +52,36 @@ const struct virtual_region *find_text_region(unsigned long addr)
+
+ void register_virtual_region(struct virtual_region *r)
+ {
+- ASSERT(!local_irq_is_enabled());
++ unsigned long flags;
+
++ spin_lock_irqsave(&virtual_region_lock, flags);
+ list_add_tail_rcu(&r->list, &virtual_region_list);
++ spin_unlock_irqrestore(&virtual_region_lock, flags);
+ }
+
+-static void remove_virtual_region(struct virtual_region *r)
++/*
++ * Suggest inline so when !CONFIG_LIVEPATCH the function is not left
++ * unreachable after init code is removed.
++ */
++static void inline remove_virtual_region(struct virtual_region *r)
+ {
+ unsigned long flags;
+
+ spin_lock_irqsave(&virtual_region_lock, flags);
+ list_del_rcu(&r->list);
+ spin_unlock_irqrestore(&virtual_region_lock, flags);
+- /*
+- * We do not need to invoke call_rcu.
+- *
+- * This is due to the fact that on the deletion we have made sure
+- * to use spinlocks (to guard against somebody else calling
+- * unregister_virtual_region) and list_deletion spiced with
+- * memory barrier.
+- *
+- * That protects us from corrupting the list as the readers all
+- * use list_for_each_entry_rcu which is safe against concurrent
+- * deletions.
+- */
+ }
+
++#ifdef CONFIG_LIVEPATCH
+ void unregister_virtual_region(struct virtual_region *r)
+ {
+- /* Expected to be called from Live Patch - which has IRQs disabled. */
+- ASSERT(!local_irq_is_enabled());
+-
+ remove_virtual_region(r);
++
++ /* Assert that no CPU might be using the removed region. */
++ rcu_barrier();
+ }
+
+-#if defined(CONFIG_LIVEPATCH) && defined(CONFIG_X86)
++#ifdef CONFIG_X86
+ void relax_virtual_region_perms(void)
+ {
+ const struct virtual_region *region;
+@@ -116,7 +105,8 @@ void tighten_virtual_region_perms(void)
+ PAGE_HYPERVISOR_RX);
+ rcu_read_unlock(&rcu_virtual_region_lock);
+ }
+-#endif
++#endif /* CONFIG_X86 */
++#endif /* CONFIG_LIVEPATCH */
+
+ void __init unregister_init_virtual_region(void)
+ {
+--
+2.44.0
+