Skip to content

Commit a5f6c2a

Browse files
rpedgecohansendc
authored andcommitted
x86/shstk: Add user control-protection fault handler
A control-protection fault is triggered when a control-flow transfer attempt violates Shadow Stack or Indirect Branch Tracking constraints. For example, the return address for a RET instruction differs from the copy on the shadow stack. There already exists a control-protection fault handler for handling kernel IBT faults. Refactor this fault handler into separate user and kernel handlers, like the page fault handler. Add a control-protection handler for usermode. To avoid ifdeffery, put them both in a new file cet.c, which is compiled in the case of either of the two CET features supported in the kernel: kernel IBT or user mode shadow stack. Move some static inline functions from traps.c into a header so they can be used in cet.c. Opportunistically fix a comment in the kernel IBT part of the fault handler that is on the end of the line instead of preceding it. Keep the same behavior for the kernel side of the fault handler, except for converting a BUG to a WARN in the case of a #CP happening when the feature is missing. This unifies the behavior with the new shadow stack code, and also prevents the kernel from crashing under this situation which is potentially recoverable. The control-protection fault handler works in a similar way as the general protection fault handler. It provides the si_code SEGV_CPERR to the signal handler. 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-28-rick.p.edgecombe%40intel.com
1 parent 98cfa46 commit a5f6c2a

File tree

16 files changed

+117
-34
lines changed

16 files changed

+117
-34
lines changed

arch/arm/kernel/signal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ asmlinkage void do_rseq_syscall(struct pt_regs *regs)
682682
*/
683683
static_assert(NSIGILL == 11);
684684
static_assert(NSIGFPE == 15);
685-
static_assert(NSIGSEGV == 9);
685+
static_assert(NSIGSEGV == 10);
686686
static_assert(NSIGBUS == 5);
687687
static_assert(NSIGTRAP == 6);
688688
static_assert(NSIGCHLD == 6);

arch/arm64/kernel/signal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,7 @@ void __init minsigstksz_setup(void)
13441344
*/
13451345
static_assert(NSIGILL == 11);
13461346
static_assert(NSIGFPE == 15);
1347-
static_assert(NSIGSEGV == 9);
1347+
static_assert(NSIGSEGV == 10);
13481348
static_assert(NSIGBUS == 5);
13491349
static_assert(NSIGTRAP == 6);
13501350
static_assert(NSIGCHLD == 6);

arch/arm64/kernel/signal32.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ void compat_setup_restart_syscall(struct pt_regs *regs)
460460
*/
461461
static_assert(NSIGILL == 11);
462462
static_assert(NSIGFPE == 15);
463-
static_assert(NSIGSEGV == 9);
463+
static_assert(NSIGSEGV == 10);
464464
static_assert(NSIGBUS == 5);
465465
static_assert(NSIGTRAP == 6);
466466
static_assert(NSIGCHLD == 6);

arch/sparc/kernel/signal32.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ asmlinkage int do_sys32_sigstack(u32 u_ssptr, u32 u_ossptr, unsigned long sp)
753753
*/
754754
static_assert(NSIGILL == 11);
755755
static_assert(NSIGFPE == 15);
756-
static_assert(NSIGSEGV == 9);
756+
static_assert(NSIGSEGV == 10);
757757
static_assert(NSIGBUS == 5);
758758
static_assert(NSIGTRAP == 6);
759759
static_assert(NSIGCHLD == 6);

arch/sparc/kernel/signal_64.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long
562562
*/
563563
static_assert(NSIGILL == 11);
564564
static_assert(NSIGFPE == 15);
565-
static_assert(NSIGSEGV == 9);
565+
static_assert(NSIGSEGV == 10);
566566
static_assert(NSIGBUS == 5);
567567
static_assert(NSIGTRAP == 6);
568568
static_assert(NSIGCHLD == 6);

arch/x86/include/asm/disabled-features.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@
111111
#define DISABLE_USER_SHSTK (1 << (X86_FEATURE_USER_SHSTK & 31))
112112
#endif
113113

114+
#ifdef CONFIG_X86_KERNEL_IBT
115+
#define DISABLE_IBT 0
116+
#else
117+
#define DISABLE_IBT (1 << (X86_FEATURE_IBT & 31))
118+
#endif
119+
114120
/*
115121
* Make sure to add features to the correct mask
116122
*/
@@ -134,7 +140,7 @@
134140
#define DISABLED_MASK16 (DISABLE_PKU|DISABLE_OSPKE|DISABLE_LA57|DISABLE_UMIP| \
135141
DISABLE_ENQCMD)
136142
#define DISABLED_MASK17 0
137-
#define DISABLED_MASK18 0
143+
#define DISABLED_MASK18 (DISABLE_IBT)
138144
#define DISABLED_MASK19 0
139145
#define DISABLED_MASK20 0
140146
#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 21)

arch/x86/include/asm/idtentry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ DECLARE_IDTENTRY_RAW_ERRORCODE(X86_TRAP_DF, xenpv_exc_double_fault);
614614
#endif
615615

616616
/* #CP */
617-
#ifdef CONFIG_X86_KERNEL_IBT
617+
#ifdef CONFIG_X86_CET
618618
DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_CP, exc_control_protection);
619619
#endif
620620

arch/x86/include/asm/traps.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,16 @@ void __noreturn handle_stack_overflow(struct pt_regs *regs,
4747
struct stack_info *info);
4848
#endif
4949

50+
static inline void cond_local_irq_enable(struct pt_regs *regs)
51+
{
52+
if (regs->flags & X86_EFLAGS_IF)
53+
local_irq_enable();
54+
}
55+
56+
static inline void cond_local_irq_disable(struct pt_regs *regs)
57+
{
58+
if (regs->flags & X86_EFLAGS_IF)
59+
local_irq_disable();
60+
}
61+
5062
#endif /* _ASM_X86_TRAPS_H */

arch/x86/kernel/cet.c

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
#include <asm/bugs.h>
55
#include <asm/traps.h>
66

7-
static __ro_after_init bool ibt_fatal = true;
8-
9-
extern void ibt_selftest_ip(void); /* code label defined in asm below */
10-
117
enum cp_error_code {
128
CP_EC = (1 << 15) - 1,
139

@@ -20,15 +16,80 @@ enum cp_error_code {
2016
CP_ENCL = 1 << 15,
2117
};
2218

23-
DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
19+
static const char cp_err[][10] = {
20+
[0] = "unknown",
21+
[1] = "near ret",
22+
[2] = "far/iret",
23+
[3] = "endbranch",
24+
[4] = "rstorssp",
25+
[5] = "setssbsy",
26+
};
27+
28+
static const char *cp_err_string(unsigned long error_code)
29+
{
30+
unsigned int cpec = error_code & CP_EC;
31+
32+
if (cpec >= ARRAY_SIZE(cp_err))
33+
cpec = 0;
34+
return cp_err[cpec];
35+
}
36+
37+
static void do_unexpected_cp(struct pt_regs *regs, unsigned long error_code)
38+
{
39+
WARN_ONCE(1, "Unexpected %s #CP, error_code: %s\n",
40+
user_mode(regs) ? "user mode" : "kernel mode",
41+
cp_err_string(error_code));
42+
}
43+
44+
static DEFINE_RATELIMIT_STATE(cpf_rate, DEFAULT_RATELIMIT_INTERVAL,
45+
DEFAULT_RATELIMIT_BURST);
46+
47+
static void do_user_cp_fault(struct pt_regs *regs, unsigned long error_code)
2448
{
25-
if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
26-
pr_err("Unexpected #CP\n");
27-
BUG();
49+
struct task_struct *tsk;
50+
unsigned long ssp;
51+
52+
/*
53+
* An exception was just taken from userspace. Since interrupts are disabled
54+
* here, no scheduling should have messed with the registers yet and they
55+
* will be whatever is live in userspace. So read the SSP before enabling
56+
* interrupts so locking the fpregs to do it later is not required.
57+
*/
58+
rdmsrl(MSR_IA32_PL3_SSP, ssp);
59+
60+
cond_local_irq_enable(regs);
61+
62+
tsk = current;
63+
tsk->thread.error_code = error_code;
64+
tsk->thread.trap_nr = X86_TRAP_CP;
65+
66+
/* Ratelimit to prevent log spamming. */
67+
if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
68+
__ratelimit(&cpf_rate)) {
69+
pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)%s",
70+
tsk->comm, task_pid_nr(tsk),
71+
regs->ip, regs->sp, ssp, error_code,
72+
cp_err_string(error_code),
73+
error_code & CP_ENCL ? " in enclave" : "");
74+
print_vma_addr(KERN_CONT " in ", regs->ip);
75+
pr_cont("\n");
2876
}
2977

30-
if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) != CP_ENDBR))
78+
force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
79+
cond_local_irq_disable(regs);
80+
}
81+
82+
static __ro_after_init bool ibt_fatal = true;
83+
84+
/* code label defined in asm below */
85+
extern void ibt_selftest_ip(void);
86+
87+
static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
88+
{
89+
if ((error_code & CP_EC) != CP_ENDBR) {
90+
do_unexpected_cp(regs, error_code);
3191
return;
92+
}
3293

3394
if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
3495
regs->ax = 0;
@@ -74,3 +135,18 @@ static int __init ibt_setup(char *str)
74135
}
75136

76137
__setup("ibt=", ibt_setup);
138+
139+
DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
140+
{
141+
if (user_mode(regs)) {
142+
if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
143+
do_user_cp_fault(regs, error_code);
144+
else
145+
do_unexpected_cp(regs, error_code);
146+
} else {
147+
if (cpu_feature_enabled(X86_FEATURE_IBT))
148+
do_kernel_cp_fault(regs, error_code);
149+
else
150+
do_unexpected_cp(regs, error_code);
151+
}
152+
}

arch/x86/kernel/idt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ static const __initconst struct idt_data def_idts[] = {
107107
ISTG(X86_TRAP_MC, asm_exc_machine_check, IST_INDEX_MCE),
108108
#endif
109109

110-
#ifdef CONFIG_X86_KERNEL_IBT
110+
#ifdef CONFIG_X86_CET
111111
INTG(X86_TRAP_CP, asm_exc_control_protection),
112112
#endif
113113

0 commit comments

Comments
 (0)