| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | #include <linux/extable.h> |
|---|
| 2 | 3 | #include <linux/uaccess.h> |
|---|
| 3 | 4 | #include <linux/sched/debug.h> |
|---|
| 4 | 5 | #include <xen/xen.h> |
|---|
| 5 | 6 | |
|---|
| 6 | 7 | #include <asm/fpu/internal.h> |
|---|
| 8 | +#include <asm/sev-es.h> |
|---|
| 7 | 9 | #include <asm/traps.h> |
|---|
| 8 | 10 | #include <asm/kdebug.h> |
|---|
| 9 | 11 | |
|---|
| 10 | 12 | typedef bool (*ex_handler_t)(const struct exception_table_entry *, |
|---|
| 11 | | - struct pt_regs *, int); |
|---|
| 13 | + struct pt_regs *, int, unsigned long, |
|---|
| 14 | + unsigned long); |
|---|
| 12 | 15 | |
|---|
| 13 | 16 | static inline unsigned long |
|---|
| 14 | 17 | ex_fixup_addr(const struct exception_table_entry *x) |
|---|
| .. | .. |
|---|
| 22 | 25 | } |
|---|
| 23 | 26 | |
|---|
| 24 | 27 | __visible bool ex_handler_default(const struct exception_table_entry *fixup, |
|---|
| 25 | | - struct pt_regs *regs, int trapnr) |
|---|
| 28 | + struct pt_regs *regs, int trapnr, |
|---|
| 29 | + unsigned long error_code, |
|---|
| 30 | + unsigned long fault_addr) |
|---|
| 26 | 31 | { |
|---|
| 27 | 32 | regs->ip = ex_fixup_addr(fixup); |
|---|
| 28 | 33 | return true; |
|---|
| .. | .. |
|---|
| 30 | 35 | EXPORT_SYMBOL(ex_handler_default); |
|---|
| 31 | 36 | |
|---|
| 32 | 37 | __visible bool ex_handler_fault(const struct exception_table_entry *fixup, |
|---|
| 33 | | - struct pt_regs *regs, int trapnr) |
|---|
| 38 | + struct pt_regs *regs, int trapnr, |
|---|
| 39 | + unsigned long error_code, |
|---|
| 40 | + unsigned long fault_addr) |
|---|
| 34 | 41 | { |
|---|
| 35 | 42 | regs->ip = ex_fixup_addr(fixup); |
|---|
| 36 | 43 | regs->ax = trapnr; |
|---|
| 37 | 44 | return true; |
|---|
| 38 | 45 | } |
|---|
| 39 | 46 | EXPORT_SYMBOL_GPL(ex_handler_fault); |
|---|
| 40 | | - |
|---|
| 41 | | -/* |
|---|
| 42 | | - * Handler for UD0 exception following a failed test against the |
|---|
| 43 | | - * result of a refcount inc/dec/add/sub. |
|---|
| 44 | | - */ |
|---|
| 45 | | -__visible bool ex_handler_refcount(const struct exception_table_entry *fixup, |
|---|
| 46 | | - struct pt_regs *regs, int trapnr) |
|---|
| 47 | | -{ |
|---|
| 48 | | - /* First unconditionally saturate the refcount. */ |
|---|
| 49 | | - *(int *)regs->cx = INT_MIN / 2; |
|---|
| 50 | | - |
|---|
| 51 | | - /* |
|---|
| 52 | | - * Strictly speaking, this reports the fixup destination, not |
|---|
| 53 | | - * the fault location, and not the actually overflowing |
|---|
| 54 | | - * instruction, which is the instruction before the "js", but |
|---|
| 55 | | - * since that instruction could be a variety of lengths, just |
|---|
| 56 | | - * report the location after the overflow, which should be close |
|---|
| 57 | | - * enough for finding the overflow, as it's at least back in |
|---|
| 58 | | - * the function, having returned from .text.unlikely. |
|---|
| 59 | | - */ |
|---|
| 60 | | - regs->ip = ex_fixup_addr(fixup); |
|---|
| 61 | | - |
|---|
| 62 | | - /* |
|---|
| 63 | | - * This function has been called because either a negative refcount |
|---|
| 64 | | - * value was seen by any of the refcount functions, or a zero |
|---|
| 65 | | - * refcount value was seen by refcount_dec(). |
|---|
| 66 | | - * |
|---|
| 67 | | - * If we crossed from INT_MAX to INT_MIN, OF (Overflow Flag: result |
|---|
| 68 | | - * wrapped around) will be set. Additionally, seeing the refcount |
|---|
| 69 | | - * reach 0 will set ZF (Zero Flag: result was zero). In each of |
|---|
| 70 | | - * these cases we want a report, since it's a boundary condition. |
|---|
| 71 | | - * The SF case is not reported since it indicates post-boundary |
|---|
| 72 | | - * manipulations below zero or above INT_MAX. And if none of the |
|---|
| 73 | | - * flags are set, something has gone very wrong, so report it. |
|---|
| 74 | | - */ |
|---|
| 75 | | - if (regs->flags & (X86_EFLAGS_OF | X86_EFLAGS_ZF)) { |
|---|
| 76 | | - bool zero = regs->flags & X86_EFLAGS_ZF; |
|---|
| 77 | | - |
|---|
| 78 | | - refcount_error_report(regs, zero ? "hit zero" : "overflow"); |
|---|
| 79 | | - } else if ((regs->flags & X86_EFLAGS_SF) == 0) { |
|---|
| 80 | | - /* Report if none of OF, ZF, nor SF are set. */ |
|---|
| 81 | | - refcount_error_report(regs, "unexpected saturation"); |
|---|
| 82 | | - } |
|---|
| 83 | | - |
|---|
| 84 | | - return true; |
|---|
| 85 | | -} |
|---|
| 86 | | -EXPORT_SYMBOL(ex_handler_refcount); |
|---|
| 87 | 47 | |
|---|
| 88 | 48 | /* |
|---|
| 89 | 49 | * Handler for when we fail to restore a task's FPU state. We should never get |
|---|
| .. | .. |
|---|
| 96 | 56 | * out all the FPU registers) if we can't restore from the task's FPU state. |
|---|
| 97 | 57 | */ |
|---|
| 98 | 58 | __visible bool ex_handler_fprestore(const struct exception_table_entry *fixup, |
|---|
| 99 | | - struct pt_regs *regs, int trapnr) |
|---|
| 59 | + struct pt_regs *regs, int trapnr, |
|---|
| 60 | + unsigned long error_code, |
|---|
| 61 | + unsigned long fault_addr) |
|---|
| 100 | 62 | { |
|---|
| 101 | 63 | regs->ip = ex_fixup_addr(fixup); |
|---|
| 102 | 64 | |
|---|
| .. | .. |
|---|
| 108 | 70 | } |
|---|
| 109 | 71 | EXPORT_SYMBOL_GPL(ex_handler_fprestore); |
|---|
| 110 | 72 | |
|---|
| 111 | | -__visible bool ex_handler_ext(const struct exception_table_entry *fixup, |
|---|
| 112 | | - struct pt_regs *regs, int trapnr) |
|---|
| 73 | +__visible bool ex_handler_uaccess(const struct exception_table_entry *fixup, |
|---|
| 74 | + struct pt_regs *regs, int trapnr, |
|---|
| 75 | + unsigned long error_code, |
|---|
| 76 | + unsigned long fault_addr) |
|---|
| 113 | 77 | { |
|---|
| 114 | | - /* Special hack for uaccess_err */ |
|---|
| 115 | | - current->thread.uaccess_err = 1; |
|---|
| 78 | + WARN_ONCE(trapnr == X86_TRAP_GP, "General protection fault in user access. Non-canonical address?"); |
|---|
| 116 | 79 | regs->ip = ex_fixup_addr(fixup); |
|---|
| 117 | 80 | return true; |
|---|
| 118 | 81 | } |
|---|
| 119 | | -EXPORT_SYMBOL(ex_handler_ext); |
|---|
| 82 | +EXPORT_SYMBOL(ex_handler_uaccess); |
|---|
| 83 | + |
|---|
| 84 | +__visible bool ex_handler_copy(const struct exception_table_entry *fixup, |
|---|
| 85 | + struct pt_regs *regs, int trapnr, |
|---|
| 86 | + unsigned long error_code, |
|---|
| 87 | + unsigned long fault_addr) |
|---|
| 88 | +{ |
|---|
| 89 | + WARN_ONCE(trapnr == X86_TRAP_GP, "General protection fault in user access. Non-canonical address?"); |
|---|
| 90 | + regs->ip = ex_fixup_addr(fixup); |
|---|
| 91 | + regs->ax = trapnr; |
|---|
| 92 | + return true; |
|---|
| 93 | +} |
|---|
| 94 | +EXPORT_SYMBOL(ex_handler_copy); |
|---|
| 120 | 95 | |
|---|
| 121 | 96 | __visible bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup, |
|---|
| 122 | | - struct pt_regs *regs, int trapnr) |
|---|
| 97 | + struct pt_regs *regs, int trapnr, |
|---|
| 98 | + unsigned long error_code, |
|---|
| 99 | + unsigned long fault_addr) |
|---|
| 123 | 100 | { |
|---|
| 124 | | - if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pF)\n", |
|---|
| 101 | + if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pS)\n", |
|---|
| 125 | 102 | (unsigned int)regs->cx, regs->ip, (void *)regs->ip)) |
|---|
| 126 | 103 | show_stack_regs(regs); |
|---|
| 127 | 104 | |
|---|
| .. | .. |
|---|
| 134 | 111 | EXPORT_SYMBOL(ex_handler_rdmsr_unsafe); |
|---|
| 135 | 112 | |
|---|
| 136 | 113 | __visible bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup, |
|---|
| 137 | | - struct pt_regs *regs, int trapnr) |
|---|
| 114 | + struct pt_regs *regs, int trapnr, |
|---|
| 115 | + unsigned long error_code, |
|---|
| 116 | + unsigned long fault_addr) |
|---|
| 138 | 117 | { |
|---|
| 139 | | - if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pF)\n", |
|---|
| 118 | + if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pS)\n", |
|---|
| 140 | 119 | (unsigned int)regs->cx, (unsigned int)regs->dx, |
|---|
| 141 | 120 | (unsigned int)regs->ax, regs->ip, (void *)regs->ip)) |
|---|
| 142 | 121 | show_stack_regs(regs); |
|---|
| .. | .. |
|---|
| 148 | 127 | EXPORT_SYMBOL(ex_handler_wrmsr_unsafe); |
|---|
| 149 | 128 | |
|---|
| 150 | 129 | __visible bool ex_handler_clear_fs(const struct exception_table_entry *fixup, |
|---|
| 151 | | - struct pt_regs *regs, int trapnr) |
|---|
| 130 | + struct pt_regs *regs, int trapnr, |
|---|
| 131 | + unsigned long error_code, |
|---|
| 132 | + unsigned long fault_addr) |
|---|
| 152 | 133 | { |
|---|
| 153 | 134 | if (static_cpu_has(X86_BUG_NULL_SEG)) |
|---|
| 154 | 135 | asm volatile ("mov %0, %%fs" : : "rm" (__USER_DS)); |
|---|
| 155 | 136 | asm volatile ("mov %0, %%fs" : : "rm" (0)); |
|---|
| 156 | | - return ex_handler_default(fixup, regs, trapnr); |
|---|
| 137 | + return ex_handler_default(fixup, regs, trapnr, error_code, fault_addr); |
|---|
| 157 | 138 | } |
|---|
| 158 | 139 | EXPORT_SYMBOL(ex_handler_clear_fs); |
|---|
| 159 | 140 | |
|---|
| 160 | | -__visible bool ex_has_fault_handler(unsigned long ip) |
|---|
| 141 | +enum handler_type ex_get_fault_handler_type(unsigned long ip) |
|---|
| 161 | 142 | { |
|---|
| 162 | 143 | const struct exception_table_entry *e; |
|---|
| 163 | 144 | ex_handler_t handler; |
|---|
| 164 | 145 | |
|---|
| 165 | 146 | e = search_exception_tables(ip); |
|---|
| 166 | 147 | if (!e) |
|---|
| 167 | | - return false; |
|---|
| 148 | + return EX_HANDLER_NONE; |
|---|
| 168 | 149 | handler = ex_fixup_handler(e); |
|---|
| 169 | | - |
|---|
| 170 | | - return handler == ex_handler_fault; |
|---|
| 150 | + if (handler == ex_handler_fault) |
|---|
| 151 | + return EX_HANDLER_FAULT; |
|---|
| 152 | + else if (handler == ex_handler_uaccess || handler == ex_handler_copy) |
|---|
| 153 | + return EX_HANDLER_UACCESS; |
|---|
| 154 | + else |
|---|
| 155 | + return EX_HANDLER_OTHER; |
|---|
| 171 | 156 | } |
|---|
| 172 | 157 | |
|---|
| 173 | 158 | __nocfi |
|---|
| 174 | | -int fixup_exception(struct pt_regs *regs, int trapnr) |
|---|
| 159 | +int fixup_exception(struct pt_regs *regs, int trapnr, unsigned long error_code, |
|---|
| 160 | + unsigned long fault_addr) |
|---|
| 175 | 161 | { |
|---|
| 176 | 162 | const struct exception_table_entry *e; |
|---|
| 177 | 163 | ex_handler_t handler; |
|---|
| .. | .. |
|---|
| 195 | 181 | return 0; |
|---|
| 196 | 182 | |
|---|
| 197 | 183 | handler = ex_fixup_handler(e); |
|---|
| 198 | | - return handler(e, regs, trapnr); |
|---|
| 184 | + return handler(e, regs, trapnr, error_code, fault_addr); |
|---|
| 199 | 185 | } |
|---|
| 200 | 186 | |
|---|
| 201 | 187 | extern unsigned int early_recursion_flag; |
|---|
| .. | .. |
|---|
| 231 | 217 | * result in a hard-to-debug panic. |
|---|
| 232 | 218 | * |
|---|
| 233 | 219 | * Keep in mind that not all vectors actually get here. Early |
|---|
| 234 | | - * fage faults, for example, are special. |
|---|
| 220 | + * page faults, for example, are special. |
|---|
| 235 | 221 | */ |
|---|
| 236 | | - if (fixup_exception(regs, trapnr)) |
|---|
| 222 | + if (fixup_exception(regs, trapnr, regs->orig_ax, 0)) |
|---|
| 237 | 223 | return; |
|---|
| 238 | 224 | |
|---|
| 239 | | - if (fixup_bug(regs, trapnr)) |
|---|
| 240 | | - return; |
|---|
| 225 | + if (trapnr == X86_TRAP_UD) { |
|---|
| 226 | + if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) { |
|---|
| 227 | + /* Skip the ud2. */ |
|---|
| 228 | + regs->ip += LEN_UD2; |
|---|
| 229 | + return; |
|---|
| 230 | + } |
|---|
| 231 | + |
|---|
| 232 | + /* |
|---|
| 233 | + * If this was a BUG and report_bug returns or if this |
|---|
| 234 | + * was just a normal #UD, we want to continue onward and |
|---|
| 235 | + * crash. |
|---|
| 236 | + */ |
|---|
| 237 | + } |
|---|
| 241 | 238 | |
|---|
| 242 | 239 | fail: |
|---|
| 243 | 240 | early_printk("PANIC: early exception 0x%02x IP %lx:%lx error %lx cr2 0x%lx\n", |
|---|