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 /0023-x86-account-for-shadow-stack-in-exception-from-stub-.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 '0023-x86-account-for-shadow-stack-in-exception-from-stub-.patch')
-rw-r--r--0023-x86-account-for-shadow-stack-in-exception-from-stub-.patch212
1 files changed, 212 insertions, 0 deletions
diff --git a/0023-x86-account-for-shadow-stack-in-exception-from-stub-.patch b/0023-x86-account-for-shadow-stack-in-exception-from-stub-.patch
new file mode 100644
index 0000000..e23a764
--- /dev/null
+++ b/0023-x86-account-for-shadow-stack-in-exception-from-stub-.patch
@@ -0,0 +1,212 @@
+From 49f77602373b58b7bbdb40cea2b49d2f88d4003d Mon Sep 17 00:00:00 2001
+From: Jan Beulich <jbeulich@suse.com>
+Date: Tue, 27 Feb 2024 14:12:11 +0100
+Subject: [PATCH 23/67] x86: account for shadow stack in exception-from-stub
+ recovery
+
+Dealing with exceptions raised from within emulation stubs involves
+discarding return address (replaced by exception related information).
+Such discarding of course also requires removing the corresponding entry
+from the shadow stack.
+
+Also amend the comment in fixup_exception_return(), to further clarify
+why use of ptr[1] can't be an out-of-bounds access.
+
+This is CVE-2023-46841 / XSA-451.
+
+Fixes: 209fb9919b50 ("x86/extable: Adjust extable handling to be shadow stack compatible")
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
+master commit: 91f5f7a9154919a765c3933521760acffeddbf28
+master date: 2024-02-27 13:49:22 +0100
+---
+ xen/arch/x86/extable.c | 20 ++++++----
+ xen/arch/x86/include/asm/uaccess.h | 3 +-
+ xen/arch/x86/traps.c | 63 +++++++++++++++++++++++++++---
+ 3 files changed, 71 insertions(+), 15 deletions(-)
+
+diff --git a/xen/arch/x86/extable.c b/xen/arch/x86/extable.c
+index 6758ba1dca..dd9583f2a5 100644
+--- a/xen/arch/x86/extable.c
++++ b/xen/arch/x86/extable.c
+@@ -86,26 +86,29 @@ search_one_extable(const struct exception_table_entry *first,
+ }
+
+ unsigned long
+-search_exception_table(const struct cpu_user_regs *regs)
++search_exception_table(const struct cpu_user_regs *regs, unsigned long *stub_ra)
+ {
+ const struct virtual_region *region = find_text_region(regs->rip);
+ unsigned long stub = this_cpu(stubs.addr);
+
+ if ( region && region->ex )
++ {
++ *stub_ra = 0;
+ return search_one_extable(region->ex, region->ex_end, regs->rip);
++ }
+
+ if ( regs->rip >= stub + STUB_BUF_SIZE / 2 &&
+ regs->rip < stub + STUB_BUF_SIZE &&
+ regs->rsp > (unsigned long)regs &&
+ regs->rsp < (unsigned long)get_cpu_info() )
+ {
+- unsigned long retptr = *(unsigned long *)regs->rsp;
++ unsigned long retaddr = *(unsigned long *)regs->rsp, fixup;
+
+- region = find_text_region(retptr);
+- retptr = region && region->ex
+- ? search_one_extable(region->ex, region->ex_end, retptr)
+- : 0;
+- if ( retptr )
++ region = find_text_region(retaddr);
++ fixup = region && region->ex
++ ? search_one_extable(region->ex, region->ex_end, retaddr)
++ : 0;
++ if ( fixup )
+ {
+ /*
+ * Put trap number and error code on the stack (in place of the
+@@ -117,7 +120,8 @@ search_exception_table(const struct cpu_user_regs *regs)
+ };
+
+ *(unsigned long *)regs->rsp = token.raw;
+- return retptr;
++ *stub_ra = retaddr;
++ return fixup;
+ }
+ }
+
+diff --git a/xen/arch/x86/include/asm/uaccess.h b/xen/arch/x86/include/asm/uaccess.h
+index 684fccd95c..74bb222c03 100644
+--- a/xen/arch/x86/include/asm/uaccess.h
++++ b/xen/arch/x86/include/asm/uaccess.h
+@@ -421,7 +421,8 @@ union stub_exception_token {
+ unsigned long raw;
+ };
+
+-extern unsigned long search_exception_table(const struct cpu_user_regs *regs);
++extern unsigned long search_exception_table(const struct cpu_user_regs *regs,
++ unsigned long *stub_ra);
+ extern void sort_exception_tables(void);
+ extern void sort_exception_table(struct exception_table_entry *start,
+ const struct exception_table_entry *stop);
+diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
+index 06c4f3868b..7599bee361 100644
+--- a/xen/arch/x86/traps.c
++++ b/xen/arch/x86/traps.c
+@@ -856,7 +856,7 @@ void do_unhandled_trap(struct cpu_user_regs *regs)
+ }
+
+ static void fixup_exception_return(struct cpu_user_regs *regs,
+- unsigned long fixup)
++ unsigned long fixup, unsigned long stub_ra)
+ {
+ if ( IS_ENABLED(CONFIG_XEN_SHSTK) )
+ {
+@@ -873,7 +873,8 @@ static void fixup_exception_return(struct cpu_user_regs *regs,
+ /*
+ * Search for %rip. The shstk currently looks like this:
+ *
+- * ... [Likely pointed to by SSP]
++ * tok [Supervisor token, == &tok | BUSY, only with FRED inactive]
++ * ... [Pointed to by SSP for most exceptions, empty in IST cases]
+ * %cs [== regs->cs]
+ * %rip [== regs->rip]
+ * SSP [Likely points to 3 slots higher, above %cs]
+@@ -891,7 +892,56 @@ static void fixup_exception_return(struct cpu_user_regs *regs,
+ */
+ if ( ptr[0] == regs->rip && ptr[1] == regs->cs )
+ {
++ unsigned long primary_shstk =
++ (ssp & ~(STACK_SIZE - 1)) +
++ (PRIMARY_SHSTK_SLOT + 1) * PAGE_SIZE - 8;
++
+ wrss(fixup, ptr);
++
++ if ( !stub_ra )
++ goto shstk_done;
++
++ /*
++ * Stub recovery ought to happen only when the outer context
++ * was on the main shadow stack. We need to also "pop" the
++ * stub's return address from the interrupted context's shadow
++ * stack. That is,
++ * - if we're still on the main stack, we need to move the
++ * entire stack (up to and including the exception frame)
++ * up by one slot, incrementing the original SSP in the
++ * exception frame,
++ * - if we're on an IST stack, we need to increment the
++ * original SSP.
++ */
++ BUG_ON((ptr[-1] ^ primary_shstk) >> PAGE_SHIFT);
++
++ if ( (ssp ^ primary_shstk) >> PAGE_SHIFT )
++ {
++ /*
++ * We're on an IST stack. First make sure the two return
++ * addresses actually match. Then increment the interrupted
++ * context's SSP.
++ */
++ BUG_ON(stub_ra != *(unsigned long*)ptr[-1]);
++ wrss(ptr[-1] + 8, &ptr[-1]);
++ goto shstk_done;
++ }
++
++ /* Make sure the two return addresses actually match. */
++ BUG_ON(stub_ra != ptr[2]);
++
++ /* Move exception frame, updating SSP there. */
++ wrss(ptr[1], &ptr[2]); /* %cs */
++ wrss(ptr[0], &ptr[1]); /* %rip */
++ wrss(ptr[-1] + 8, &ptr[0]); /* SSP */
++
++ /* Move all newer entries. */
++ while ( --ptr != _p(ssp) )
++ wrss(ptr[-1], &ptr[0]);
++
++ /* Finally account for our own stack having shifted up. */
++ asm volatile ( "incsspd %0" :: "r" (2) );
++
+ goto shstk_done;
+ }
+ }
+@@ -912,7 +962,8 @@ static void fixup_exception_return(struct cpu_user_regs *regs,
+
+ static bool extable_fixup(struct cpu_user_regs *regs, bool print)
+ {
+- unsigned long fixup = search_exception_table(regs);
++ unsigned long stub_ra = 0;
++ unsigned long fixup = search_exception_table(regs, &stub_ra);
+
+ if ( unlikely(fixup == 0) )
+ return false;
+@@ -926,7 +977,7 @@ static bool extable_fixup(struct cpu_user_regs *regs, bool print)
+ vector_name(regs->entry_vector), regs->error_code,
+ _p(regs->rip), _p(regs->rip), _p(fixup));
+
+- fixup_exception_return(regs, fixup);
++ fixup_exception_return(regs, fixup, stub_ra);
+ this_cpu(last_extable_addr) = regs->rip;
+
+ return true;
+@@ -1214,7 +1265,7 @@ void do_invalid_op(struct cpu_user_regs *regs)
+ void (*fn)(struct cpu_user_regs *) = bug_ptr(bug);
+
+ fn(regs);
+- fixup_exception_return(regs, (unsigned long)eip);
++ fixup_exception_return(regs, (unsigned long)eip, 0);
+ return;
+ }
+
+@@ -1235,7 +1286,7 @@ void do_invalid_op(struct cpu_user_regs *regs)
+ case BUGFRAME_warn:
+ printk("Xen WARN at %s%s:%d\n", prefix, filename, lineno);
+ show_execution_state(regs);
+- fixup_exception_return(regs, (unsigned long)eip);
++ fixup_exception_return(regs, (unsigned long)eip, 0);
+ return;
+
+ case BUGFRAME_bug:
+--
+2.44.0
+