[RFC][PATCH 19/24] x86/process: Initialize cloned task shadow stack
From: Dave Hansen
Date: Wed Oct 21 2026 - 15:56:20 EST
A child task can obviously not share its shadow stack with its parent.
It needs its own shadow stack.
First, ensure that the child's ->kshstk_ssp is pointed to its own
shadow stack. Second, write a pointer to the shadow stack itself that
corresponds to the RSP value consumed in ret_from_fork_asm.
Note that this implementation uses the direct map alias of the new
task's shadow stack. It would probably be best to use WRSS. But
shadow stacks may not be enabled on the current CPU before the
context switch making WRSS unavailable.
An implementation that universally enabled shadow stacks and made
the ISA available at the first context switch could use WRSS. This
is something else that will be simpler if the init task gets a working
shadow stack.
TODO: I don't like the #ifdef in the .c file, but there needs to
be an #ifdef somewhere for 'kshstk_ssp' since it is not defined when
shadow stacks are disabled.
---
b/arch/x86/kernel/process.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff -puN arch/x86/kernel/process.c~enable-task-kssp arch/x86/kernel/process.c
--- a/arch/x86/kernel/process.c~enable-task-kssp 2026-10-21 12:36:42.071888995 -0700
+++ b/arch/x86/kernel/process.c 2026-10-21 12:36:42.074889001 -0700
@@ -167,6 +167,40 @@ __visible void ret_from_fork(struct task
syscall_exit_to_user_mode(regs);
}
+/*
+ * The goal here is to build a shadow stack for the new thread. It
+ * just needs to survive the first RET from ret_from_fork_asm().
+ */
+static void prep_kernel_shadow_stack(struct task_struct *tsk)
+{
+#ifdef CONFIG_X86_KERN_SHADOW_STACK
+ unsigned long *ss_top_slot;
+ u8 *shstk_base_writable;
+ u8 *shstk_base_vmap;
+ u8 *ss_top;
+
+ if (!cpu_feature_enabled(X86_FEATURE_KERN_SHSTK))
+ return;
+
+ /* Start with the shadow stack permission mapping: */
+ shstk_base_vmap = &task_thread_union(tsk)->stacks.shadow_stack[0];
+
+ /* Walk the page tables and find the direct map alias of the sshstk */
+ shstk_base_writable = page_address(vmalloc_to_page(shstk_base_vmap));
+
+ /* Point at the end of the shadow stack: */
+ ss_top = shstk_base_writable + TASK_SHADOW_STACK_SIZE;
+ /* Back up one ulong from the end to write the first return pointer: */
+ ss_top_slot = (unsigned long *)ss_top - 1;
+
+ /* Write the return address for the first RET: */
+ *ss_top_slot = (unsigned long)ret_from_fork_asm;
+
+ // Needs helper?
+ tsk->thread.kshstk_ssp = shstk_base_vmap + TASK_SHADOW_STACK_SIZE - sizeof(unsigned long);
+#endif
+}
+
int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
{
u64 clone_flags = args->flags;
@@ -185,6 +219,7 @@ int copy_thread(struct task_struct *p, c
frame->bp = encode_frame_pointer(childregs);
frame->ret_addr = (unsigned long) ret_from_fork_asm;
p->thread.sp = (unsigned long) fork_frame;
+ prep_kernel_shadow_stack(p);
p->thread.io_bitmap = NULL;
clear_tsk_thread_flag(p, TIF_IO_BITMAP);
p->thread.iopl_warn = 0;
_