Skip to content

Commit 2d39a6a

Browse files
rpedgecohansendc
authored andcommitted
x86/shstk: Add user-mode shadow stack support
Introduce basic shadow stack enabling/disabling/allocation routines. A task's shadow stack is allocated from memory with VM_SHADOW_STACK flag and has a fixed size of min(RLIMIT_STACK, 4GB). Keep the task's shadow stack address and size in thread_struct. This will be copied when cloning new threads, but needs to be cleared during exec, so add a function to do this. 32 bit shadow stack is not expected to have many users and it will complicate the signal implementation. So do not support IA32 emulation or x32. Co-developed-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Kees Cook <keescook@chromium.org> Acked-by: Mike Rapoport (IBM) <rppt@kernel.org> Tested-by: Pengfei Xu <pengfei.xu@intel.com> Tested-by: John Allen <john.allen@amd.com> Tested-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/all/20230613001108.3040476-29-rick.p.edgecombe%40intel.com
1 parent a5f6c2a commit 2d39a6a

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

arch/x86/include/asm/processor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ struct thread_struct {
479479
#ifdef CONFIG_X86_USER_SHADOW_STACK
480480
unsigned long features;
481481
unsigned long features_locked;
482+
483+
struct thread_shstk shstk;
482484
#endif
483485

484486
/* Floating point and extended processor state */

arch/x86/include/asm/shstk.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@
88
struct task_struct;
99

1010
#ifdef CONFIG_X86_USER_SHADOW_STACK
11+
struct thread_shstk {
12+
u64 base;
13+
u64 size;
14+
};
15+
1116
long shstk_prctl(struct task_struct *task, int option, unsigned long features);
1217
void reset_thread_features(void);
18+
void shstk_free(struct task_struct *p);
1319
#else
1420
static inline long shstk_prctl(struct task_struct *task, int option,
1521
unsigned long arg2) { return -EINVAL; }
1622
static inline void reset_thread_features(void) {}
23+
static inline void shstk_free(struct task_struct *p) {}
1724
#endif /* CONFIG_X86_USER_SHADOW_STACK */
1825

1926
#endif /* __ASSEMBLY__ */

arch/x86/include/uapi/asm/prctl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@
3434
#define ARCH_SHSTK_DISABLE 0x5002
3535
#define ARCH_SHSTK_LOCK 0x5003
3636

37+
/* ARCH_SHSTK_ features bits */
38+
#define ARCH_SHSTK_SHSTK (1ULL << 0)
39+
3740
#endif /* _ASM_X86_PRCTL_H */

arch/x86/kernel/shstk.c

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,159 @@
88

99
#include <linux/sched.h>
1010
#include <linux/bitops.h>
11+
#include <linux/types.h>
12+
#include <linux/mm.h>
13+
#include <linux/mman.h>
14+
#include <linux/slab.h>
15+
#include <linux/uaccess.h>
16+
#include <linux/sched/signal.h>
17+
#include <linux/compat.h>
18+
#include <linux/sizes.h>
19+
#include <linux/user.h>
20+
#include <asm/msr.h>
21+
#include <asm/fpu/xstate.h>
22+
#include <asm/fpu/types.h>
23+
#include <asm/shstk.h>
24+
#include <asm/special_insns.h>
25+
#include <asm/fpu/api.h>
1126
#include <asm/prctl.h>
1227

28+
static bool features_enabled(unsigned long features)
29+
{
30+
return current->thread.features & features;
31+
}
32+
33+
static void features_set(unsigned long features)
34+
{
35+
current->thread.features |= features;
36+
}
37+
38+
static void features_clr(unsigned long features)
39+
{
40+
current->thread.features &= ~features;
41+
}
42+
43+
static unsigned long alloc_shstk(unsigned long size)
44+
{
45+
int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_ABOVE4G;
46+
struct mm_struct *mm = current->mm;
47+
unsigned long addr, unused;
48+
49+
mmap_write_lock(mm);
50+
addr = do_mmap(NULL, addr, size, PROT_READ, flags,
51+
VM_SHADOW_STACK | VM_WRITE, 0, &unused, NULL);
52+
53+
mmap_write_unlock(mm);
54+
55+
return addr;
56+
}
57+
58+
static unsigned long adjust_shstk_size(unsigned long size)
59+
{
60+
if (size)
61+
return PAGE_ALIGN(size);
62+
63+
return PAGE_ALIGN(min_t(unsigned long long, rlimit(RLIMIT_STACK), SZ_4G));
64+
}
65+
66+
static void unmap_shadow_stack(u64 base, u64 size)
67+
{
68+
while (1) {
69+
int r;
70+
71+
r = vm_munmap(base, size);
72+
73+
/*
74+
* vm_munmap() returns -EINTR when mmap_lock is held by
75+
* something else, and that lock should not be held for a
76+
* long time. Retry it for the case.
77+
*/
78+
if (r == -EINTR) {
79+
cond_resched();
80+
continue;
81+
}
82+
83+
/*
84+
* For all other types of vm_munmap() failure, either the
85+
* system is out of memory or there is bug.
86+
*/
87+
WARN_ON_ONCE(r);
88+
break;
89+
}
90+
}
91+
92+
static int shstk_setup(void)
93+
{
94+
struct thread_shstk *shstk = &current->thread.shstk;
95+
unsigned long addr, size;
96+
97+
/* Already enabled */
98+
if (features_enabled(ARCH_SHSTK_SHSTK))
99+
return 0;
100+
101+
/* Also not supported for 32 bit and x32 */
102+
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) || in_32bit_syscall())
103+
return -EOPNOTSUPP;
104+
105+
size = adjust_shstk_size(0);
106+
addr = alloc_shstk(size);
107+
if (IS_ERR_VALUE(addr))
108+
return PTR_ERR((void *)addr);
109+
110+
fpregs_lock_and_load();
111+
wrmsrl(MSR_IA32_PL3_SSP, addr + size);
112+
wrmsrl(MSR_IA32_U_CET, CET_SHSTK_EN);
113+
fpregs_unlock();
114+
115+
shstk->base = addr;
116+
shstk->size = size;
117+
features_set(ARCH_SHSTK_SHSTK);
118+
119+
return 0;
120+
}
121+
13122
void reset_thread_features(void)
14123
{
124+
memset(&current->thread.shstk, 0, sizeof(struct thread_shstk));
15125
current->thread.features = 0;
16126
current->thread.features_locked = 0;
17127
}
18128

129+
void shstk_free(struct task_struct *tsk)
130+
{
131+
struct thread_shstk *shstk = &tsk->thread.shstk;
132+
133+
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
134+
!features_enabled(ARCH_SHSTK_SHSTK))
135+
return;
136+
137+
if (!tsk->mm)
138+
return;
139+
140+
unmap_shadow_stack(shstk->base, shstk->size);
141+
}
142+
143+
static int shstk_disable(void)
144+
{
145+
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
146+
return -EOPNOTSUPP;
147+
148+
/* Already disabled? */
149+
if (!features_enabled(ARCH_SHSTK_SHSTK))
150+
return 0;
151+
152+
fpregs_lock_and_load();
153+
/* Disable WRSS too when disabling shadow stack */
154+
wrmsrl(MSR_IA32_U_CET, 0);
155+
wrmsrl(MSR_IA32_PL3_SSP, 0);
156+
fpregs_unlock();
157+
158+
shstk_free(current);
159+
features_clr(ARCH_SHSTK_SHSTK);
160+
161+
return 0;
162+
}
163+
19164
long shstk_prctl(struct task_struct *task, int option, unsigned long features)
20165
{
21166
if (option == ARCH_SHSTK_LOCK) {

0 commit comments

Comments
 (0)