Skip to content

Commit 9280547

Browse files
rpedgecohansendc
authored andcommitted
x86/shstk: Introduce routines modifying shstk
Shadow stacks are normally written to via CALL/RET or specific CET instructions like RSTORSSP/SAVEPREVSSP. However, sometimes the kernel will need to write to the shadow stack directly using the ring-0 only WRUSS instruction. A shadow stack restore token marks a restore point of the shadow stack, and the address in a token must point directly above the token, which is within the same shadow stack. This is distinctively different from other pointers on the shadow stack, since those pointers point to executable code area. Introduce token setup and verify routines. Also introduce WRUSS, which is a kernel-mode instruction but writes directly to user shadow stack. In future patches that enable shadow stack to work with signals, the kernel will need something to denote the point in the stack where sigreturn may be called. This will prevent attackers calling sigreturn at arbitrary places in the stack, in order to help prevent SROP attacks. To do this, something that can only be written by the kernel needs to be placed on the shadow stack. This can be accomplished by setting bit 63 in the frame written to the shadow stack. Userspace return addresses can't have this bit set as it is in the kernel range. It also can't be a valid restore token. 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-31-rick.p.edgecombe%40intel.com
1 parent b2926a3 commit 9280547

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

arch/x86/include/asm/special_insns.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,19 @@ static inline void clwb(volatile void *__p)
202202
: [pax] "a" (p));
203203
}
204204

205+
#ifdef CONFIG_X86_USER_SHADOW_STACK
206+
static inline int write_user_shstk_64(u64 __user *addr, u64 val)
207+
{
208+
asm_volatile_goto("1: wrussq %[val], (%[addr])\n"
209+
_ASM_EXTABLE(1b, %l[fail])
210+
:: [addr] "r" (addr), [val] "r" (val)
211+
:: fail);
212+
return 0;
213+
fail:
214+
return -EFAULT;
215+
}
216+
#endif /* CONFIG_X86_USER_SHADOW_STACK */
217+
205218
#define nop() asm volatile ("nop")
206219

207220
static inline void serialize(void)

arch/x86/kernel/shstk.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <asm/fpu/api.h>
2626
#include <asm/prctl.h>
2727

28+
#define SS_FRAME_SIZE 8
29+
2830
static bool features_enabled(unsigned long features)
2931
{
3032
return current->thread.features & features;
@@ -40,6 +42,35 @@ static void features_clr(unsigned long features)
4042
current->thread.features &= ~features;
4143
}
4244

45+
/*
46+
* Create a restore token on the shadow stack. A token is always 8-byte
47+
* and aligned to 8.
48+
*/
49+
static int create_rstor_token(unsigned long ssp, unsigned long *token_addr)
50+
{
51+
unsigned long addr;
52+
53+
/* Token must be aligned */
54+
if (!IS_ALIGNED(ssp, 8))
55+
return -EINVAL;
56+
57+
addr = ssp - SS_FRAME_SIZE;
58+
59+
/*
60+
* SSP is aligned, so reserved bits and mode bit are a zero, just mark
61+
* the token 64-bit.
62+
*/
63+
ssp |= BIT(0);
64+
65+
if (write_user_shstk_64((u64 __user *)addr, (u64)ssp))
66+
return -EFAULT;
67+
68+
if (token_addr)
69+
*token_addr = addr;
70+
71+
return 0;
72+
}
73+
4374
static unsigned long alloc_shstk(unsigned long size)
4475
{
4576
int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_ABOVE4G;
@@ -157,6 +188,50 @@ unsigned long shstk_alloc_thread_stack(struct task_struct *tsk, unsigned long cl
157188
return addr + size;
158189
}
159190

191+
static unsigned long get_user_shstk_addr(void)
192+
{
193+
unsigned long long ssp;
194+
195+
fpregs_lock_and_load();
196+
197+
rdmsrl(MSR_IA32_PL3_SSP, ssp);
198+
199+
fpregs_unlock();
200+
201+
return ssp;
202+
}
203+
204+
#define SHSTK_DATA_BIT BIT(63)
205+
206+
static int put_shstk_data(u64 __user *addr, u64 data)
207+
{
208+
if (WARN_ON_ONCE(data & SHSTK_DATA_BIT))
209+
return -EINVAL;
210+
211+
/*
212+
* Mark the high bit so that the sigframe can't be processed as a
213+
* return address.
214+
*/
215+
if (write_user_shstk_64(addr, data | SHSTK_DATA_BIT))
216+
return -EFAULT;
217+
return 0;
218+
}
219+
220+
static int get_shstk_data(unsigned long *data, unsigned long __user *addr)
221+
{
222+
unsigned long ldata;
223+
224+
if (unlikely(get_user(ldata, addr)))
225+
return -EFAULT;
226+
227+
if (!(ldata & SHSTK_DATA_BIT))
228+
return -EINVAL;
229+
230+
*data = ldata & ~SHSTK_DATA_BIT;
231+
232+
return 0;
233+
}
234+
160235
void shstk_free(struct task_struct *tsk)
161236
{
162237
struct thread_shstk *shstk = &tsk->thread.shstk;

0 commit comments

Comments
 (0)