~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/x86/kernel/cet.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 
  3 #include <linux/ptrace.h>
  4 #include <asm/bugs.h>
  5 #include <asm/traps.h>
  6 
  7 enum cp_error_code {
  8         CP_EC        = (1 << 15) - 1,
  9 
 10         CP_RET       = 1,
 11         CP_IRET      = 2,
 12         CP_ENDBR     = 3,
 13         CP_RSTRORSSP = 4,
 14         CP_SETSSBSY  = 5,
 15 
 16         CP_ENCL      = 1 << 15,
 17 };
 18 
 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)
 48 {
 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");
 76         }
 77 
 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 static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
 85 {
 86         if ((error_code & CP_EC) != CP_ENDBR) {
 87                 do_unexpected_cp(regs, error_code);
 88                 return;
 89         }
 90 
 91         if (unlikely(regs->ip == (unsigned long)&ibt_selftest_noendbr)) {
 92                 regs->ax = 0;
 93                 return;
 94         }
 95 
 96         pr_err("Missing ENDBR: %pS\n", (void *)instruction_pointer(regs));
 97         if (!ibt_fatal) {
 98                 printk(KERN_DEFAULT CUT_HERE);
 99                 __warn(__FILE__, __LINE__, (void *)regs->ip, TAINT_WARN, regs, NULL);
100                 return;
101         }
102         BUG();
103 }
104 
105 static int __init ibt_setup(char *str)
106 {
107         if (!strcmp(str, "off"))
108                 setup_clear_cpu_cap(X86_FEATURE_IBT);
109 
110         if (!strcmp(str, "warn"))
111                 ibt_fatal = false;
112 
113         return 1;
114 }
115 
116 __setup("ibt=", ibt_setup);
117 
118 DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
119 {
120         if (user_mode(regs)) {
121                 if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK))
122                         do_user_cp_fault(regs, error_code);
123                 else
124                         do_unexpected_cp(regs, error_code);
125         } else {
126                 if (cpu_feature_enabled(X86_FEATURE_IBT))
127                         do_kernel_cp_fault(regs, error_code);
128                 else
129                         do_unexpected_cp(regs, error_code);
130         }
131 }
132 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php