[RFC][PATCH 14/24] x86/mm: Establish shadow stack permissions in thread mapping
From: Dave Hansen
Date: Wed Oct 21 2026 - 15:55:09 EST
This represents the simplest way to establish a per-task shadow stack
mapping. It is not an efficient way to do it. It costs a TLB flush
each time a stack is allocated which is wicked stupid and slow. But,
it's simple and will function for now. Fix it properly later by doing:
vm_area = __get_vm_area_node()
pages = kmalloc(); // the array
vmap(PAGE_KERNEL) // 16k
vmap(PAGE_KERNEL_SHSTK) // 4k
That way, there's no TLB flush, and the shadow stack permissions are
set up properly. The shadow stack is allocated in the same way as the
normal stack, and the permissions are set up correctly
== Optimization ==
It might be nice to turn arch_init_thread_stacks() into a static call
to keep the overhead down.
Nit: I don't love the #ifdef in shstk.c, but it's over a tiny area.
---
b/arch/x86/include/asm/thread_info.h | 7 +++++++
b/arch/x86/kernel/shstk.c | 21 +++++++++++++++++++++
2 files changed, 28 insertions(+)
diff -puN arch/x86/include/asm/thread_info.h~x86-set-shadow-stack-permissions arch/x86/include/asm/thread_info.h
--- a/arch/x86/include/asm/thread_info.h~x86-set-shadow-stack-permissions 2026-10-21 12:36:39.397883874 -0700
+++ b/arch/x86/include/asm/thread_info.h 2026-10-21 12:36:39.409883897 -0700
@@ -226,6 +226,13 @@ static inline int arch_within_stack_fram
extern void arch_setup_new_exec(void);
#define arch_setup_new_exec arch_setup_new_exec
+
+#ifdef CONFIG_X86_KERN_SHADOW_STACK
+struct thread_stacks;
+extern void arch_init_thread_stacks(struct thread_stacks *ts);
+#define arch_init_thread_stacks arch_init_thread_stacks
+#endif
+
#endif /* !__ASSEMBLER__ */
#endif /* _ASM_X86_THREAD_INFO_H */
diff -puN arch/x86/kernel/shstk.c~x86-set-shadow-stack-permissions arch/x86/kernel/shstk.c
--- a/arch/x86/kernel/shstk.c~x86-set-shadow-stack-permissions 2026-10-21 12:36:39.406883891 -0700
+++ b/arch/x86/kernel/shstk.c 2026-10-21 12:36:39.409883897 -0700
@@ -630,3 +630,24 @@ bool shstk_is_enabled(void)
{
return features_enabled(ARCH_SHSTK_SHSTK);
}
+
+#ifdef CONFIG_X86_KERN_SHADOW_STACK
+static inline int set_shstk_pte(pte_t *ptep, unsigned long addr, void *data)
+{
+ set_pte(ptep, pte_mkwrite_shstk(pte_wrprotect(ptep_get(ptep))));
+ return 0;
+}
+
+void arch_init_thread_stacks(struct thread_stacks *ts)
+{
+ unsigned long shstk_vaddr = (unsigned long)&ts->shadow_stack;
+ unsigned long shstk_size = sizeof(ts->shadow_stack);
+
+ if (!cpu_feature_enabled(X86_FEATURE_KERN_SHSTK))
+ return;
+
+ apply_to_existing_page_range(&init_mm, shstk_vaddr, shstk_size,
+ set_shstk_pte, NULL);
+ flush_tlb_kernel_range(shstk_vaddr, shstk_vaddr + shstk_size);
+}
+#endif
_