[RFC][PATCH 20/24] x86/entry: Stash kernel SSP on FRED entry

From: Dave Hansen

Date: Wed Oct 21 2026 - 15:56:40 EST



Kernel shadow stacks are intended to preserve control flow integrity
across all kinds of RIP transitions, not just strict CALLs. One of
those transitions is when there is a kernel interrupt or exception
and the kernel stops what it was doing and jumps over to
asm_fred_entrypoint_kernel.

On those jumps, FRED hardware writes a special frame to the shadow
stack. That frame includes the return address of the interrupted
kernel code. When returning from the event via ERETS, the hardware
compares this shadow stack frame with the return address on the
regular stack to ensure that they match.

The kernel munges event return addresses for lots of reasons. But,
with shadow stacks, it also needs to munge the corresponding
shadow stack frame contents.

Make it easy for RIP-munging code to locate the shadow stack frame.
Stash off the shadow stack pointer so it sits adjacent to pt_regs.
Without this, anything wishing to locate the special shadow stack
frame would need to walk up the stack. I'm not opposed to doing
it that way, this just seemed simpler for now.

Note, since RDSSPQ is a NOP with shadow stacks off, this assembly
is safe either way, although it does a unnecessary push/pop.

---

b/arch/x86/entry/entry_64_fred.S | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff -puN arch/x86/entry/entry_64_fred.S~fred-entry-ssp-stash arch/x86/entry/entry_64_fred.S
--- a/arch/x86/entry/entry_64_fred.S~fred-entry-ssp-stash 2026-10-21 12:36:42.552889917 -0700
+++ b/arch/x86/entry/entry_64_fred.S 2026-10-21 12:36:42.555889923 -0700
@@ -16,6 +16,26 @@
.code64
.section .noinstr.text, "ax"

+/*
+ * Kernel-mode events: save the SSP of the shadow stack frame FRED
+ * pushed (old SSP, LIP, CS) just below pt_regs. See struct
+ * fred_kernel_frame.
+ */
+.macro PUSH_KERNEL_EVENT_SSP
+#ifdef CONFIG_X86_USER_SHADOW_STACK
+ xorl %eax, %eax
+ rdsspq %rax /* nop if shadow stacks are off */
+ pushq %rax
+ UNWIND_HINT_REGS offset=8
+#endif
+.endm
+
+.macro POP_KERNEL_EVENT_SSP
+#ifdef CONFIG_X86_USER_SHADOW_STACK
+ addq $8, %rsp
+#endif
+.endm
+
.macro FRED_ENTER
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR
@@ -54,7 +74,9 @@ SYM_CODE_END(asm_fred_entrypoint_user)
.org asm_fred_entrypoint_user + 256, 0xcc
SYM_CODE_START_NOALIGN(asm_fred_entrypoint_kernel)
FRED_ENTER
+ PUSH_KERNEL_EVENT_SSP
call fred_entry_from_kernel
+ POP_KERNEL_EVENT_SSP
FRED_EXIT
ERETS
SYM_CODE_END(asm_fred_entrypoint_kernel)
_