Skip to content

Commit 05e3602

Browse files
rpedgecohansendc
authored andcommitted
x86/shstk: Handle signals for shadow stack
When a signal is handled, the context is pushed to the stack before handling it. For shadow stacks, since the shadow stack only tracks return addresses, there isn't any state that needs to be pushed. However, there are still a few things that need to be done. These things are visible to userspace and which will be kernel ABI for shadow stacks. One is to make sure the restorer address is written to shadow stack, since the signal handler (if not changing ucontext) returns to the restorer, and the restorer calls sigreturn. So add the restorer on the shadow stack before handling the signal, so there is not a conflict when the signal handler returns to the restorer. The other thing to do is to place some type of checkable token on the thread's shadow stack before handling the signal and check it during sigreturn. This is an extra layer of protection to hamper attackers calling sigreturn manually as in SROP-like attacks. For this token the shadow stack data format defined earlier can be used. Have the data pushed be the previous SSP. In the future the sigreturn might want to return back to a different stack. Storing the SSP (instead of a restore offset or something) allows for future functionality that may want to restore to a different stack. So, when handling a signal push - the SSP pointing in the shadow stack data format - the restorer address below the restore token. In sigreturn, verify SSP is stored in the data format and pop the shadow stack. 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-32-rick.p.edgecombe%40intel.com
1 parent 9280547 commit 05e3602

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

arch/x86/include/asm/shstk.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/types.h>
77

88
struct task_struct;
9+
struct ksignal;
910

1011
#ifdef CONFIG_X86_USER_SHADOW_STACK
1112
struct thread_shstk {
@@ -18,6 +19,8 @@ void reset_thread_features(void);
1819
unsigned long shstk_alloc_thread_stack(struct task_struct *p, unsigned long clone_flags,
1920
unsigned long stack_size);
2021
void shstk_free(struct task_struct *p);
22+
int setup_signal_shadow_stack(struct ksignal *ksig);
23+
int restore_signal_shadow_stack(void);
2124
#else
2225
static inline long shstk_prctl(struct task_struct *task, int option,
2326
unsigned long arg2) { return -EINVAL; }
@@ -26,6 +29,8 @@ static inline unsigned long shstk_alloc_thread_stack(struct task_struct *p,
2629
unsigned long clone_flags,
2730
unsigned long stack_size) { return 0; }
2831
static inline void shstk_free(struct task_struct *p) {}
32+
static inline int setup_signal_shadow_stack(struct ksignal *ksig) { return 0; }
33+
static inline int restore_signal_shadow_stack(void) { return 0; }
2934
#endif /* CONFIG_X86_USER_SHADOW_STACK */
3035

3136
#endif /* __ASSEMBLY__ */

arch/x86/kernel/shstk.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,101 @@ static int get_shstk_data(unsigned long *data, unsigned long __user *addr)
232232
return 0;
233233
}
234234

235+
static int shstk_push_sigframe(unsigned long *ssp)
236+
{
237+
unsigned long target_ssp = *ssp;
238+
239+
/* Token must be aligned */
240+
if (!IS_ALIGNED(target_ssp, 8))
241+
return -EINVAL;
242+
243+
*ssp -= SS_FRAME_SIZE;
244+
if (put_shstk_data((void *__user)*ssp, target_ssp))
245+
return -EFAULT;
246+
247+
return 0;
248+
}
249+
250+
static int shstk_pop_sigframe(unsigned long *ssp)
251+
{
252+
unsigned long token_addr;
253+
int err;
254+
255+
err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp);
256+
if (unlikely(err))
257+
return err;
258+
259+
/* Restore SSP aligned? */
260+
if (unlikely(!IS_ALIGNED(token_addr, 8)))
261+
return -EINVAL;
262+
263+
/* SSP in userspace? */
264+
if (unlikely(token_addr >= TASK_SIZE_MAX))
265+
return -EINVAL;
266+
267+
*ssp = token_addr;
268+
269+
return 0;
270+
}
271+
272+
int setup_signal_shadow_stack(struct ksignal *ksig)
273+
{
274+
void __user *restorer = ksig->ka.sa.sa_restorer;
275+
unsigned long ssp;
276+
int err;
277+
278+
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
279+
!features_enabled(ARCH_SHSTK_SHSTK))
280+
return 0;
281+
282+
if (!restorer)
283+
return -EINVAL;
284+
285+
ssp = get_user_shstk_addr();
286+
if (unlikely(!ssp))
287+
return -EINVAL;
288+
289+
err = shstk_push_sigframe(&ssp);
290+
if (unlikely(err))
291+
return err;
292+
293+
/* Push restorer address */
294+
ssp -= SS_FRAME_SIZE;
295+
err = write_user_shstk_64((u64 __user *)ssp, (u64)restorer);
296+
if (unlikely(err))
297+
return -EFAULT;
298+
299+
fpregs_lock_and_load();
300+
wrmsrl(MSR_IA32_PL3_SSP, ssp);
301+
fpregs_unlock();
302+
303+
return 0;
304+
}
305+
306+
int restore_signal_shadow_stack(void)
307+
{
308+
unsigned long ssp;
309+
int err;
310+
311+
if (!cpu_feature_enabled(X86_FEATURE_USER_SHSTK) ||
312+
!features_enabled(ARCH_SHSTK_SHSTK))
313+
return 0;
314+
315+
ssp = get_user_shstk_addr();
316+
if (unlikely(!ssp))
317+
return -EINVAL;
318+
319+
err = shstk_pop_sigframe(&ssp);
320+
if (unlikely(err))
321+
return err;
322+
323+
fpregs_lock_and_load();
324+
wrmsrl(MSR_IA32_PL3_SSP, ssp);
325+
fpregs_unlock();
326+
327+
return 0;
328+
}
329+
235330
void shstk_free(struct task_struct *tsk)
236331
{
237332
struct thread_shstk *shstk = &tsk->thread.shstk;

arch/x86/kernel/signal.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <asm/syscall.h>
4141
#include <asm/sigframe.h>
4242
#include <asm/signal.h>
43+
#include <asm/shstk.h>
4344

4445
static inline int is_ia32_compat_frame(struct ksignal *ksig)
4546
{

arch/x86/kernel/signal_64.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
175175
frame = get_sigframe(ksig, regs, sizeof(struct rt_sigframe), &fp);
176176
uc_flags = frame_uc_flags(regs);
177177

178+
if (setup_signal_shadow_stack(ksig))
179+
return -EFAULT;
180+
178181
if (!user_access_begin(frame, sizeof(*frame)))
179182
return -EFAULT;
180183

@@ -260,6 +263,9 @@ SYSCALL_DEFINE0(rt_sigreturn)
260263
if (!restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
261264
goto badframe;
262265

266+
if (restore_signal_shadow_stack())
267+
goto badframe;
268+
263269
if (restore_altstack(&frame->uc.uc_stack))
264270
goto badframe;
265271

0 commit comments

Comments
 (0)