[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen staging] x86/thunk: Build Xen with Return Thunks



commit afcb4a06c740f7f71d2e9746c9d147c38a6e6c90
Author:     Jan Beulich <jbeulich@xxxxxxxx>
AuthorDate: Mon Apr 7 17:15:17 2025 +0200
Commit:     Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Mon May 12 17:17:38 2025 +0100

    x86/thunk: Build Xen with Return Thunks
    
    The Indirect Target Selection speculative vulnerability means that indirect
    branches (including RETs) are unsafe when in the first half of a cacheline.
    
    In order to mitigate this, build with return thunks and arrange for
    __x86_return_thunk to be (mis)aligned in the same manner as
    __x86_indirect_thunk_* so the RET instruction is placed in a safe location.
    
    place_ret() needs to conditionally emit JMP __x86_return_thunk instead of 
RET.
    
    This is part of XSA-469 / CVE-2024-28956
    
    Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
    Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
    Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
 xen/arch/x86/Kconfig                 |  5 +++++
 xen/arch/x86/Makefile                |  1 +
 xen/arch/x86/acpi/wakeup_prot.S      |  2 +-
 xen/arch/x86/alternative.c           | 31 ++++++++++++++++++++++++++++++-
 xen/arch/x86/arch.mk                 |  3 +++
 xen/arch/x86/bhb-thunk.S             |  2 +-
 xen/arch/x86/clear_page.S            |  4 +++-
 xen/arch/x86/copy_page.S             |  4 +++-
 xen/arch/x86/efi/check.c             |  3 +++
 xen/arch/x86/include/asm/asm-defns.h |  6 ++++++
 xen/arch/x86/indirect-thunk.S        | 24 ++++++++++++++++++++++++
 xen/arch/x86/pv/emul-priv-op.c       |  2 +-
 xen/arch/x86/pv/gpr_switch.S         |  4 ++--
 xen/arch/x86/spec_ctrl.c             |  3 +++
 xen/arch/x86/x86_64/compat/entry.S   |  6 +++---
 xen/arch/x86/x86_64/entry.S          |  2 +-
 xen/arch/x86/xen.lds.S               |  1 +
 xen/common/Kconfig                   | 11 +++++++++++
 xen/lib/x86-generic-hweightl.c       |  4 ++++
 19 files changed, 106 insertions(+), 12 deletions(-)

diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index de2fa37f08..7afe879710 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -39,9 +39,14 @@ config ARCH_DEFCONFIG
        default "arch/x86/configs/x86_64_defconfig"
 
 config CC_HAS_INDIRECT_THUNK
+       # GCC >= 8 or Clang >= 6
        def_bool $(cc-option,-mindirect-branch-register) || \
                 $(cc-option,-mretpoline-external-thunk)
 
+config CC_HAS_RETURN_THUNK
+       # GCC >= 8 or Clang >= 15
+       def_bool $(cc-option,-mfunction-return=thunk-extern)
+
 config HAS_AS_CET_SS
        # binutils >= 2.29 or LLVM >= 6
        def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy)
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index c32aba029d..bedb97cbee 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o
 obj-y += msi.o
 obj-y += msr.o
 obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o
+obj-$(CONFIG_RETURN_THUNK) += indirect-thunk.o
 obj-$(CONFIG_PV) += ioport_emulate.o
 obj-y += irq.o
 obj-$(CONFIG_KEXEC) += machine_kexec.o
diff --git a/xen/arch/x86/acpi/wakeup_prot.S b/xen/arch/x86/acpi/wakeup_prot.S
index a741d58b91..92af6230b3 100644
--- a/xen/arch/x86/acpi/wakeup_prot.S
+++ b/xen/arch/x86/acpi/wakeup_prot.S
@@ -133,7 +133,7 @@ LABEL(s3_resume)
         pop     %r12
         pop     %rbx
         pop     %rbp
-        ret
+        RET
 END(do_suspend_lowlevel)
 
 .data
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index 0449b9c1b7..ecc56964bd 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -135,16 +135,45 @@ void init_or_livepatch add_nops(void *insns, unsigned int 
len)
     }
 }
 
+void nocall __x86_return_thunk(void);
+
 /*
  * Place a return at @ptr.  @ptr must be in the writable alias of a stub.
  *
+ * When CONFIG_RETURN_THUNK is active, this may be a JMP __x86_return_thunk
+ * instead, depending on the safety of @ptr with respect to Indirect Target
+ * Selection.
+ *
  * Returns the next position to write into the stub.
  */
 void *place_ret(void *ptr)
 {
+    unsigned long addr = (unsigned long)ptr;
     uint8_t *p = ptr;
 
-    *p++ = 0xc3;
+    /*
+     * When Return Thunks are used, if a RET would be unsafe at this location
+     * with respect to Indirect Target Selection (i.e. if addr is in the first
+     * half of a cacheline), insert a JMP __x86_return_thunk instead.
+     *
+     * The displacement needs to be relative to the executable alias of the
+     * stub, not to @ptr which is the writeable alias.
+     */
+    if ( IS_ENABLED(CONFIG_RETURN_THUNK) && !(addr & 0x20) )
+    {
+        long stub_va = (this_cpu(stubs.addr) & PAGE_MASK) + (addr & 
~PAGE_MASK);
+        long disp = (long)__x86_return_thunk - (stub_va + 5);
+
+        BUG_ON((int32_t)disp != disp);
+
+        *p++ = 0xe9;
+        *(int32_t *)p = disp;
+        p += 4;
+    }
+    else
+    {
+        *p++ = 0xc3;
+    }
 
     return p;
 }
diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk
index 09b6d8758e..4991545125 100644
--- a/xen/arch/x86/arch.mk
+++ b/xen/arch/x86/arch.mk
@@ -34,6 +34,9 @@ CFLAGS-$(CONFIG_CC_IS_GCC) += -fno-jump-tables
 CFLAGS-$(CONFIG_CC_IS_CLANG) += -mretpoline-external-thunk
 endif
 
+# Compile with return thunk support if selected.
+CFLAGS-$(CONFIG_RETURN_THUNK) += -mfunction-return=thunk-extern
+
 # Disable the addition of a .note.gnu.property section to object files when
 # livepatch support is enabled.  The contents of that section can change
 # depending on the instructions used, and livepatch-build-tools doesn't know
diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S
index 7175778bdd..606b378d84 100644
--- a/xen/arch/x86/bhb-thunk.S
+++ b/xen/arch/x86/bhb-thunk.S
@@ -23,7 +23,7 @@ FUNC(clear_bhb_tsx)
         xabort  $0
         int3
 1:
-        ret
+        RET
 END(clear_bhb_tsx)
 
 /*
diff --git a/xen/arch/x86/clear_page.S b/xen/arch/x86/clear_page.S
index d6c076f1d8..dc3c3c26bf 100644
--- a/xen/arch/x86/clear_page.S
+++ b/xen/arch/x86/clear_page.S
@@ -1,6 +1,8 @@
         .file __FILE__
 
 #include <xen/linkage.h>
+
+#include <asm/asm_defns.h>
 #include <asm/page.h>
 
 FUNC(clear_page_sse2)
@@ -16,5 +18,5 @@ FUNC(clear_page_sse2)
         jnz     0b
 
         sfence
-        ret
+        RET
 END(clear_page_sse2)
diff --git a/xen/arch/x86/copy_page.S b/xen/arch/x86/copy_page.S
index c3c436545b..e43e5370c8 100644
--- a/xen/arch/x86/copy_page.S
+++ b/xen/arch/x86/copy_page.S
@@ -1,6 +1,8 @@
         .file __FILE__
 
 #include <xen/linkage.h>
+
+#include <asm/asm_defns.h>
 #include <asm/page.h>
 
 #define src_reg %rsi
@@ -41,5 +43,5 @@ FUNC(copy_page_sse2)
         movnti  tmp4_reg, 3*WORD_SIZE(dst_reg)
 
         sfence
-        ret
+        RET
 END(copy_page_sse2)
diff --git a/xen/arch/x86/efi/check.c b/xen/arch/x86/efi/check.c
index 9e473faad3..23ba30abf3 100644
--- a/xen/arch/x86/efi/check.c
+++ b/xen/arch/x86/efi/check.c
@@ -3,6 +3,9 @@ int __attribute__((__ms_abi__)) test(int i)
     return i;
 }
 
+/* In case -mfunction-return is in use. */
+void __x86_return_thunk(void) {};
+
 /*
  * Populate an array with "addresses" of relocatable and absolute values.
  * This is to probe ld for (a) emitting base relocations at all and (b) not
diff --git a/xen/arch/x86/include/asm/asm-defns.h 
b/xen/arch/x86/include/asm/asm-defns.h
index 1b821db49c..96370dd92a 100644
--- a/xen/arch/x86/include/asm/asm-defns.h
+++ b/xen/arch/x86/include/asm/asm-defns.h
@@ -36,6 +36,12 @@
     .endif
 .endm
 
+#ifdef CONFIG_RETURN_THUNK
+# define RET jmp __x86_return_thunk
+#else
+# define RET ret
+#endif
+
 #ifdef CONFIG_XEN_IBT
 # define ENDBR64 endbr64
 #else
diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S
index c4b978d67b..26dad15f12 100644
--- a/xen/arch/x86/indirect-thunk.S
+++ b/xen/arch/x86/indirect-thunk.S
@@ -15,6 +15,8 @@
 #undef SYM_ALIGN
 #define SYM_ALIGN(align...)
 
+#ifdef CONFIG_INDIRECT_THUNK
+
 .macro IND_THUNK_RETPOLINE reg:req
         call 1f
         int3
@@ -62,3 +64,25 @@ END(__x86_indirect_thunk_\reg)
 .irp reg, ax, cx, dx, bx, bp, si, di, 8, 9, 10, 11, 12, 13, 14, 15
         GEN_INDIRECT_THUNK reg=r\reg
 .endr
+
+#endif /* CONFIG_INDIRECT_THUNK */
+
+#ifdef CONFIG_RETURN_THUNK
+        .section .text.entry.__x86_return_thunk, "ax", @progbits
+
+        /*
+         * The Indirect Target Selection speculative vulnerability means that
+         * indirect branches (including RETs) are unsafe when in the first
+         * half of a cacheline.  Arrange for them to be in the second half.
+         *
+         * Align to 64, then skip 32.
+         */
+        .balign 64
+        .fill 32, 1, 0xcc
+
+FUNC(__x86_return_thunk)
+        ret
+        int3 /* Halt straight-line speculation */
+END(__x86_return_thunk)
+
+#endif /* CONFIG_RETURN_THUNK */
diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c
index ff5d1c9f86..295d847ea2 100644
--- a/xen/arch/x86/pv/emul-priv-op.c
+++ b/xen/arch/x86/pv/emul-priv-op.c
@@ -131,7 +131,7 @@ static io_emul_stub_t *io_emul_stub_setup(struct 
priv_op_ctxt *ctxt, u8 opcode,
     BUILD_BUG_ON(STUB_BUF_SIZE / 2 <
                  (sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ +
                   MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) +
-                  1 /* ret */));
+                  (IS_ENABLED(CONFIG_RETURN_THUNK) ? 5 : 1) /* ret */));
     /* Runtime confirmation that we haven't clobbered an adjacent stub. */
     BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub));
 
diff --git a/xen/arch/x86/pv/gpr_switch.S b/xen/arch/x86/pv/gpr_switch.S
index 5409ad3b14..362b5d2416 100644
--- a/xen/arch/x86/pv/gpr_switch.S
+++ b/xen/arch/x86/pv/gpr_switch.S
@@ -26,7 +26,7 @@ FUNC(load_guest_gprs)
         movq  UREGS_r15(%rdi), %r15
         movq  UREGS_rcx(%rdi), %rcx
         movq  UREGS_rdi(%rdi), %rdi
-        ret
+        RET
 END(load_guest_gprs)
 
 /* Save guest GPRs.  Parameter on the stack above the return address. */
@@ -48,5 +48,5 @@ FUNC(save_guest_gprs)
         movq  %rbx, UREGS_rbx(%rdi)
         movq  %rdx, UREGS_rdx(%rdi)
         movq  %rcx, UREGS_rcx(%rdi)
-        ret
+        RET
 END(save_guest_gprs)
diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c
index ced8475001..fe27e82a47 100644
--- a/xen/arch/x86/spec_ctrl.c
+++ b/xen/arch/x86/spec_ctrl.c
@@ -571,6 +571,9 @@ static void __init print_details(enum ind_thunk thunk)
 #ifdef CONFIG_INDIRECT_THUNK
                " INDIRECT_THUNK"
 #endif
+#ifdef CONFIG_RETURN_THUNK
+               " RETURN_THUNK"
+#endif
 #ifdef CONFIG_SHADOW_PAGING
                " SHADOW_PAGING"
 #endif
diff --git a/xen/arch/x86/x86_64/compat/entry.S 
b/xen/arch/x86/x86_64/compat/entry.S
index 1e87652f4b..d7b381ea54 100644
--- a/xen/arch/x86/x86_64/compat/entry.S
+++ b/xen/arch/x86/x86_64/compat/entry.S
@@ -180,7 +180,7 @@ FUNC(cr4_pv32_restore)
         or    cr4_pv32_mask(%rip), %rax
         mov   %rax, %cr4
         mov   %rax, (%rcx)
-        ret
+        RET
 0:
 #ifndef NDEBUG
         /* Check that _all_ of the bits intended to be set actually are. */
@@ -198,7 +198,7 @@ FUNC(cr4_pv32_restore)
 1:
 #endif
         xor   %eax, %eax
-        ret
+        RET
 END(cr4_pv32_restore)
 
 FUNC(compat_syscall)
@@ -329,7 +329,7 @@ __UNLIKELY_END(compat_bounce_null_selector)
         xor   %eax, %eax
         mov   %ax,  TRAPBOUNCE_cs(%rdx)
         mov   %al,  TRAPBOUNCE_flags(%rdx)
-        ret
+        RET
 
 .section .fixup,"ax"
 .Lfx13:
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
index d81a626d16..39c7b9d17f 100644
--- a/xen/arch/x86/x86_64/entry.S
+++ b/xen/arch/x86/x86_64/entry.S
@@ -604,7 +604,7 @@ __UNLIKELY_END(create_bounce_frame_bad_bounce_ip)
         xor   %eax, %eax
         mov   %rax, TRAPBOUNCE_eip(%rdx)
         mov   %al,  TRAPBOUNCE_flags(%rdx)
-        ret
+        RET
 
         .pushsection .fixup, "ax", @progbits
         # Numeric tags below represent the intended overall %rsi adjustment.
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 53bafc98a5..bf956b6c5f 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -85,6 +85,7 @@ SECTIONS
        . = ALIGN(PAGE_SIZE);
        _stextentry = .;
        *(.text.entry)
+       *(.text.entry.*)
        . = ALIGN(PAGE_SIZE);
        _etextentry = .;
 
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index bf7b081ad0..6d43be2e6e 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -172,6 +172,17 @@ config INDIRECT_THUNK
          by choosing a hardware-dependent instruction sequence to implement
          (e.g. function pointers) safely.  "Retpoline" is one such sequence.
 
+config RETURN_THUNK
+       bool "Out-of-line Returns"
+       depends on CC_HAS_RETURN_THUNK
+       default INDIRECT_THUNK
+       help
+         Compile Xen with out-of-line returns.
+
+         This allows Xen to mitigate a variety of speculative vulnerabilities
+         by choosing a hardware-dependent instruction sequence to implement
+         function returns safely.
+
 config SPECULATIVE_HARDEN_ARRAY
        bool "Speculative Array Hardening"
        default y
diff --git a/xen/lib/x86-generic-hweightl.c b/xen/lib/x86-generic-hweightl.c
index 123a5b4392..1cab68952a 100644
--- a/xen/lib/x86-generic-hweightl.c
+++ b/xen/lib/x86-generic-hweightl.c
@@ -51,7 +51,11 @@ asm (
     "pop    %rdx\n\t"
     "pop    %rdi\n\t"
 
+#ifdef CONFIG_RETURN_THUNK
+    "jmp    __x86_return_thunk\n\t"
+#else
     "ret\n\t"
+#endif
 
     ".size arch_generic_hweightl, . - arch_generic_hweightl\n\t"
 );
--
generated by git-patchbot for /home/xen/git/xen.git#staging



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.