xref: /openbmc/linux/arch/x86/kernel/cet.c (revision 9144f784f852f9a125cabe9927b986d909bfa439)
12da5b91fSRick Edgecombe // SPDX-License-Identifier: GPL-2.0
22da5b91fSRick Edgecombe 
32da5b91fSRick Edgecombe #include <linux/ptrace.h>
42da5b91fSRick Edgecombe #include <asm/bugs.h>
52da5b91fSRick Edgecombe #include <asm/traps.h>
62da5b91fSRick Edgecombe 
72da5b91fSRick Edgecombe enum cp_error_code {
82da5b91fSRick Edgecombe 	CP_EC        = (1 << 15) - 1,
92da5b91fSRick Edgecombe 
102da5b91fSRick Edgecombe 	CP_RET       = 1,
112da5b91fSRick Edgecombe 	CP_IRET      = 2,
122da5b91fSRick Edgecombe 	CP_ENDBR     = 3,
132da5b91fSRick Edgecombe 	CP_RSTRORSSP = 4,
142da5b91fSRick Edgecombe 	CP_SETSSBSY  = 5,
152da5b91fSRick Edgecombe 
162da5b91fSRick Edgecombe 	CP_ENCL	     = 1 << 15,
172da5b91fSRick Edgecombe };
182da5b91fSRick Edgecombe 
19a5f6c2acSRick Edgecombe static const char cp_err[][10] = {
20a5f6c2acSRick Edgecombe 	[0] = "unknown",
21a5f6c2acSRick Edgecombe 	[1] = "near ret",
22a5f6c2acSRick Edgecombe 	[2] = "far/iret",
23a5f6c2acSRick Edgecombe 	[3] = "endbranch",
24a5f6c2acSRick Edgecombe 	[4] = "rstorssp",
25a5f6c2acSRick Edgecombe 	[5] = "setssbsy",
26a5f6c2acSRick Edgecombe };
27a5f6c2acSRick Edgecombe 
cp_err_string(unsigned long error_code)28a5f6c2acSRick Edgecombe static const char *cp_err_string(unsigned long error_code)
292da5b91fSRick Edgecombe {
30a5f6c2acSRick Edgecombe 	unsigned int cpec = error_code & CP_EC;
31a5f6c2acSRick Edgecombe 
32a5f6c2acSRick Edgecombe 	if (cpec >= ARRAY_SIZE(cp_err))
33a5f6c2acSRick Edgecombe 		cpec = 0;
34a5f6c2acSRick Edgecombe 	return cp_err[cpec];
352da5b91fSRick Edgecombe }
362da5b91fSRick Edgecombe 
do_unexpected_cp(struct pt_regs * regs,unsigned long error_code)37a5f6c2acSRick Edgecombe static void do_unexpected_cp(struct pt_regs *regs, unsigned long error_code)
38a5f6c2acSRick Edgecombe {
39a5f6c2acSRick Edgecombe 	WARN_ONCE(1, "Unexpected %s #CP, error_code: %s\n",
40a5f6c2acSRick Edgecombe 		  user_mode(regs) ? "user mode" : "kernel mode",
41a5f6c2acSRick Edgecombe 		  cp_err_string(error_code));
42a5f6c2acSRick Edgecombe }
43a5f6c2acSRick Edgecombe 
44a5f6c2acSRick Edgecombe static DEFINE_RATELIMIT_STATE(cpf_rate, DEFAULT_RATELIMIT_INTERVAL,
45a5f6c2acSRick Edgecombe 			      DEFAULT_RATELIMIT_BURST);
46a5f6c2acSRick Edgecombe 
do_user_cp_fault(struct pt_regs * regs,unsigned long error_code)47a5f6c2acSRick Edgecombe static void do_user_cp_fault(struct pt_regs *regs, unsigned long error_code)
48a5f6c2acSRick Edgecombe {
49a5f6c2acSRick Edgecombe 	struct task_struct *tsk;
50a5f6c2acSRick Edgecombe 	unsigned long ssp;
51a5f6c2acSRick Edgecombe 
52a5f6c2acSRick Edgecombe 	/*
53a5f6c2acSRick Edgecombe 	 * An exception was just taken from userspace. Since interrupts are disabled
54a5f6c2acSRick Edgecombe 	 * here, no scheduling should have messed with the registers yet and they
55a5f6c2acSRick Edgecombe 	 * will be whatever is live in userspace. So read the SSP before enabling
56a5f6c2acSRick Edgecombe 	 * interrupts so locking the fpregs to do it later is not required.
57a5f6c2acSRick Edgecombe 	 */
58a5f6c2acSRick Edgecombe 	rdmsrl(MSR_IA32_PL3_SSP, ssp);
59a5f6c2acSRick Edgecombe 
60a5f6c2acSRick Edgecombe 	cond_local_irq_enable(regs);
61a5f6c2acSRick Edgecombe 
62a5f6c2acSRick Edgecombe 	tsk = current;
63a5f6c2acSRick Edgecombe 	tsk->thread.error_code = error_code;
64a5f6c2acSRick Edgecombe 	tsk->thread.trap_nr = X86_TRAP_CP;
65a5f6c2acSRick Edgecombe 
66a5f6c2acSRick Edgecombe 	/* Ratelimit to prevent log spamming. */
67a5f6c2acSRick Edgecombe 	if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
68a5f6c2acSRick Edgecombe 	    __ratelimit(&cpf_rate)) {
69a5f6c2acSRick Edgecombe 		pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)%s",
70a5f6c2acSRick Edgecombe 			 tsk->comm, task_pid_nr(tsk),
71a5f6c2acSRick Edgecombe 			 regs->ip, regs->sp, ssp, error_code,
72a5f6c2acSRick Edgecombe 			 cp_err_string(error_code),
73a5f6c2acSRick Edgecombe 			 error_code & CP_ENCL ? " in enclave" : "");
74a5f6c2acSRick Edgecombe 		print_vma_addr(KERN_CONT " in ", regs->ip);
75a5f6c2acSRick Edgecombe 		pr_cont("\n");
76a5f6c2acSRick Edgecombe 	}
77a5f6c2acSRick Edgecombe 
78a5f6c2acSRick Edgecombe 	force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
79a5f6c2acSRick Edgecombe 	cond_local_irq_disable(regs);
80a5f6c2acSRick Edgecombe }
81a5f6c2acSRick Edgecombe 
82a5f6c2acSRick Edgecombe static __ro_after_init bool ibt_fatal = true;
83a5f6c2acSRick Edgecombe 
84*15144785SXin Li (Intel) /*
85*15144785SXin Li (Intel)  * By definition, all missing-ENDBRANCH #CPs are a result of WFE && !ENDBR.
86*15144785SXin Li (Intel)  *
87*15144785SXin Li (Intel)  * For the kernel IBT no ENDBR selftest where #CPs are deliberately triggered,
88*15144785SXin Li (Intel)  * the WFE state of the interrupted context needs to be cleared to let execution
89*15144785SXin Li (Intel)  * continue.  Otherwise when the CPU resumes from the instruction that just
90*15144785SXin Li (Intel)  * caused the previous #CP, another missing-ENDBRANCH #CP is raised and the CPU
91*15144785SXin Li (Intel)  * enters a dead loop.
92*15144785SXin Li (Intel)  *
93*15144785SXin Li (Intel)  * This is not a problem with IDT because it doesn't preserve WFE and IRET doesn't
94*15144785SXin Li (Intel)  * set WFE.  But FRED provides space on the entry stack (in an expanded CS area)
95*15144785SXin Li (Intel)  * to save and restore the WFE state, thus the WFE state is no longer clobbered,
96*15144785SXin Li (Intel)  * so software must clear it.
97*15144785SXin Li (Intel)  */
ibt_clear_fred_wfe(struct pt_regs * regs)98*15144785SXin Li (Intel) static void ibt_clear_fred_wfe(struct pt_regs *regs)
99*15144785SXin Li (Intel) {
100*15144785SXin Li (Intel) 	/*
101*15144785SXin Li (Intel) 	 * No need to do any FRED checks.
102*15144785SXin Li (Intel) 	 *
103*15144785SXin Li (Intel) 	 * For IDT event delivery, the high-order 48 bits of CS are pushed
104*15144785SXin Li (Intel) 	 * as 0s into the stack, and later IRET ignores these bits.
105*15144785SXin Li (Intel) 	 *
106*15144785SXin Li (Intel) 	 * For FRED, a test to check if fred_cs.wfe is set would be dropped
107*15144785SXin Li (Intel) 	 * by compilers.
108*15144785SXin Li (Intel) 	 */
109*15144785SXin Li (Intel) 	regs->fred_cs.wfe = 0;
110*15144785SXin Li (Intel) }
111*15144785SXin Li (Intel) 
do_kernel_cp_fault(struct pt_regs * regs,unsigned long error_code)112a5f6c2acSRick Edgecombe static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
113a5f6c2acSRick Edgecombe {
114a5f6c2acSRick Edgecombe 	if ((error_code & CP_EC) != CP_ENDBR) {
115a5f6c2acSRick Edgecombe 		do_unexpected_cp(regs, error_code);
1162da5b91fSRick Edgecombe 		return;
117a5f6c2acSRick Edgecombe 	}
1182da5b91fSRick Edgecombe 
119c6cfcbd8SJosh Poimboeuf 	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_noendbr)) {
1202da5b91fSRick Edgecombe 		regs->ax = 0;
121*15144785SXin Li (Intel) 		ibt_clear_fred_wfe(regs);
1222da5b91fSRick Edgecombe 		return;
1232da5b91fSRick Edgecombe 	}
1242da5b91fSRick Edgecombe 
1252da5b91fSRick Edgecombe 	pr_err("Missing ENDBR: %pS\n", (void *)instruction_pointer(regs));
1262da5b91fSRick Edgecombe 	if (!ibt_fatal) {
1272da5b91fSRick Edgecombe 		printk(KERN_DEFAULT CUT_HERE);
1282da5b91fSRick Edgecombe 		__warn(__FILE__, __LINE__, (void *)regs->ip, TAINT_WARN, regs, NULL);
129*15144785SXin Li (Intel) 		ibt_clear_fred_wfe(regs);
1302da5b91fSRick Edgecombe 		return;
1312da5b91fSRick Edgecombe 	}
1322da5b91fSRick Edgecombe 	BUG();
1332da5b91fSRick Edgecombe }
1342da5b91fSRick Edgecombe 
ibt_setup(char * str)1352da5b91fSRick Edgecombe static int __init ibt_setup(char *str)
1362da5b91fSRick Edgecombe {
1372da5b91fSRick Edgecombe 	if (!strcmp(str, "off"))
1382da5b91fSRick Edgecombe 		setup_clear_cpu_cap(X86_FEATURE_IBT);
1392da5b91fSRick Edgecombe 
1402da5b91fSRick Edgecombe 	if (!strcmp(str, "warn"))
1412da5b91fSRick Edgecombe 		ibt_fatal = false;
1422da5b91fSRick Edgecombe 
1432da5b91fSRick Edgecombe 	return 1;
1442da5b91fSRick Edgecombe }
1452da5b91fSRick Edgecombe 
1462da5b91fSRick Edgecombe __setup("ibt=", ibt_setup);
147a5f6c2acSRick Edgecombe 
DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)148a5f6c2acSRick Edgecombe DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
149a5f6c2acSRick Edgecombe {
150a5f6c2acSRick Edgecombe 	if (user_mode(regs)) {
151a5f6c2acSRick Edgecombe 		if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
152a5f6c2acSRick Edgecombe 			do_user_cp_fault(regs, error_code);
153a5f6c2acSRick Edgecombe 		else
154a5f6c2acSRick Edgecombe 			do_unexpected_cp(regs, error_code);
155a5f6c2acSRick Edgecombe 	} else {
156a5f6c2acSRick Edgecombe 		if (cpu_feature_enabled(X86_FEATURE_IBT))
157a5f6c2acSRick Edgecombe 			do_kernel_cp_fault(regs, error_code);
158a5f6c2acSRick Edgecombe 		else
159a5f6c2acSRick Edgecombe 			do_unexpected_cp(regs, error_code);
160a5f6c2acSRick Edgecombe 	}
161a5f6c2acSRick Edgecombe }
162