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
do_kernel_cp_fault(struct pt_regs * regs,unsigned long error_code)84a5f6c2acSRick Edgecombe static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
85a5f6c2acSRick Edgecombe {
86a5f6c2acSRick Edgecombe if ((error_code & CP_EC) != CP_ENDBR) {
87a5f6c2acSRick Edgecombe do_unexpected_cp(regs, error_code);
882da5b91fSRick Edgecombe return;
89a5f6c2acSRick Edgecombe }
902da5b91fSRick Edgecombe
91*c6cfcbd8SJosh Poimboeuf if (unlikely(regs->ip == (unsigned long)&ibt_selftest_noendbr)) {
922da5b91fSRick Edgecombe regs->ax = 0;
932da5b91fSRick Edgecombe return;
942da5b91fSRick Edgecombe }
952da5b91fSRick Edgecombe
962da5b91fSRick Edgecombe pr_err("Missing ENDBR: %pS\n", (void *)instruction_pointer(regs));
972da5b91fSRick Edgecombe if (!ibt_fatal) {
982da5b91fSRick Edgecombe printk(KERN_DEFAULT CUT_HERE);
992da5b91fSRick Edgecombe __warn(__FILE__, __LINE__, (void *)regs->ip, TAINT_WARN, regs, NULL);
1002da5b91fSRick Edgecombe return;
1012da5b91fSRick Edgecombe }
1022da5b91fSRick Edgecombe BUG();
1032da5b91fSRick Edgecombe }
1042da5b91fSRick Edgecombe
ibt_setup(char * str)1052da5b91fSRick Edgecombe static int __init ibt_setup(char *str)
1062da5b91fSRick Edgecombe {
1072da5b91fSRick Edgecombe if (!strcmp(str, "off"))
1082da5b91fSRick Edgecombe setup_clear_cpu_cap(X86_FEATURE_IBT);
1092da5b91fSRick Edgecombe
1102da5b91fSRick Edgecombe if (!strcmp(str, "warn"))
1112da5b91fSRick Edgecombe ibt_fatal = false;
1122da5b91fSRick Edgecombe
1132da5b91fSRick Edgecombe return 1;
1142da5b91fSRick Edgecombe }
1152da5b91fSRick Edgecombe
1162da5b91fSRick Edgecombe __setup("ibt=", ibt_setup);
117a5f6c2acSRick Edgecombe
DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)118a5f6c2acSRick Edgecombe DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
119a5f6c2acSRick Edgecombe {
120a5f6c2acSRick Edgecombe if (user_mode(regs)) {
121a5f6c2acSRick Edgecombe if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
122a5f6c2acSRick Edgecombe do_user_cp_fault(regs, error_code);
123a5f6c2acSRick Edgecombe else
124a5f6c2acSRick Edgecombe do_unexpected_cp(regs, error_code);
125a5f6c2acSRick Edgecombe } else {
126a5f6c2acSRick Edgecombe if (cpu_feature_enabled(X86_FEATURE_IBT))
127a5f6c2acSRick Edgecombe do_kernel_cp_fault(regs, error_code);
128a5f6c2acSRick Edgecombe else
129a5f6c2acSRick Edgecombe do_unexpected_cp(regs, error_code);
130a5f6c2acSRick Edgecombe }
131a5f6c2acSRick Edgecombe }
132