diff options
Diffstat (limited to 'arch/x86/mm/extable.c')
-rw-r--r-- | arch/x86/mm/extable.c | 114 |
1 files changed, 100 insertions, 14 deletions
diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c index 45f5d6cf65ae..6521134057e8 100644 --- a/arch/x86/mm/extable.c +++ b/arch/x86/mm/extable.c | |||
@@ -8,7 +8,8 @@ | |||
8 | #include <asm/kdebug.h> | 8 | #include <asm/kdebug.h> |
9 | 9 | ||
10 | typedef bool (*ex_handler_t)(const struct exception_table_entry *, | 10 | typedef bool (*ex_handler_t)(const struct exception_table_entry *, |
11 | struct pt_regs *, int); | 11 | struct pt_regs *, int, unsigned long, |
12 | unsigned long); | ||
12 | 13 | ||
13 | static inline unsigned long | 14 | static inline unsigned long |
14 | ex_fixup_addr(const struct exception_table_entry *x) | 15 | ex_fixup_addr(const struct exception_table_entry *x) |
@@ -22,7 +23,9 @@ ex_fixup_handler(const struct exception_table_entry *x) | |||
22 | } | 23 | } |
23 | 24 | ||
24 | __visible bool ex_handler_default(const struct exception_table_entry *fixup, | 25 | __visible bool ex_handler_default(const struct exception_table_entry *fixup, |
25 | struct pt_regs *regs, int trapnr) | 26 | struct pt_regs *regs, int trapnr, |
27 | unsigned long error_code, | ||
28 | unsigned long fault_addr) | ||
26 | { | 29 | { |
27 | regs->ip = ex_fixup_addr(fixup); | 30 | regs->ip = ex_fixup_addr(fixup); |
28 | return true; | 31 | return true; |
@@ -30,7 +33,9 @@ __visible bool ex_handler_default(const struct exception_table_entry *fixup, | |||
30 | EXPORT_SYMBOL(ex_handler_default); | 33 | EXPORT_SYMBOL(ex_handler_default); |
31 | 34 | ||
32 | __visible bool ex_handler_fault(const struct exception_table_entry *fixup, | 35 | __visible bool ex_handler_fault(const struct exception_table_entry *fixup, |
33 | struct pt_regs *regs, int trapnr) | 36 | struct pt_regs *regs, int trapnr, |
37 | unsigned long error_code, | ||
38 | unsigned long fault_addr) | ||
34 | { | 39 | { |
35 | regs->ip = ex_fixup_addr(fixup); | 40 | regs->ip = ex_fixup_addr(fixup); |
36 | regs->ax = trapnr; | 41 | regs->ax = trapnr; |
@@ -43,7 +48,9 @@ EXPORT_SYMBOL_GPL(ex_handler_fault); | |||
43 | * result of a refcount inc/dec/add/sub. | 48 | * result of a refcount inc/dec/add/sub. |
44 | */ | 49 | */ |
45 | __visible bool ex_handler_refcount(const struct exception_table_entry *fixup, | 50 | __visible bool ex_handler_refcount(const struct exception_table_entry *fixup, |
46 | struct pt_regs *regs, int trapnr) | 51 | struct pt_regs *regs, int trapnr, |
52 | unsigned long error_code, | ||
53 | unsigned long fault_addr) | ||
47 | { | 54 | { |
48 | /* First unconditionally saturate the refcount. */ | 55 | /* First unconditionally saturate the refcount. */ |
49 | *(int *)regs->cx = INT_MIN / 2; | 56 | *(int *)regs->cx = INT_MIN / 2; |
@@ -96,7 +103,9 @@ EXPORT_SYMBOL(ex_handler_refcount); | |||
96 | * out all the FPU registers) if we can't restore from the task's FPU state. | 103 | * out all the FPU registers) if we can't restore from the task's FPU state. |
97 | */ | 104 | */ |
98 | __visible bool ex_handler_fprestore(const struct exception_table_entry *fixup, | 105 | __visible bool ex_handler_fprestore(const struct exception_table_entry *fixup, |
99 | struct pt_regs *regs, int trapnr) | 106 | struct pt_regs *regs, int trapnr, |
107 | unsigned long error_code, | ||
108 | unsigned long fault_addr) | ||
100 | { | 109 | { |
101 | regs->ip = ex_fixup_addr(fixup); | 110 | regs->ip = ex_fixup_addr(fixup); |
102 | 111 | ||
@@ -108,9 +117,79 @@ __visible bool ex_handler_fprestore(const struct exception_table_entry *fixup, | |||
108 | } | 117 | } |
109 | EXPORT_SYMBOL_GPL(ex_handler_fprestore); | 118 | EXPORT_SYMBOL_GPL(ex_handler_fprestore); |
110 | 119 | ||
120 | /* Helper to check whether a uaccess fault indicates a kernel bug. */ | ||
121 | static bool bogus_uaccess(struct pt_regs *regs, int trapnr, | ||
122 | unsigned long fault_addr) | ||
123 | { | ||
124 | /* This is the normal case: #PF with a fault address in userspace. */ | ||
125 | if (trapnr == X86_TRAP_PF && fault_addr < TASK_SIZE_MAX) | ||
126 | return false; | ||
127 | |||
128 | /* | ||
129 | * This code can be reached for machine checks, but only if the #MC | ||
130 | * handler has already decided that it looks like a candidate for fixup. | ||
131 | * This e.g. happens when attempting to access userspace memory which | ||
132 | * the CPU can't access because of uncorrectable bad memory. | ||
133 | */ | ||
134 | if (trapnr == X86_TRAP_MC) | ||
135 | return false; | ||
136 | |||
137 | /* | ||
138 | * There are two remaining exception types we might encounter here: | ||
139 | * - #PF for faulting accesses to kernel addresses | ||
140 | * - #GP for faulting accesses to noncanonical addresses | ||
141 | * Complain about anything else. | ||
142 | */ | ||
143 | if (trapnr != X86_TRAP_PF && trapnr != X86_TRAP_GP) { | ||
144 | WARN(1, "unexpected trap %d in uaccess\n", trapnr); | ||
145 | return false; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * This is a faulting memory access in kernel space, on a kernel | ||
150 | * address, in a usercopy function. This can e.g. be caused by improper | ||
151 | * use of helpers like __put_user and by improper attempts to access | ||
152 | * userspace addresses in KERNEL_DS regions. | ||
153 | * The one (semi-)legitimate exception are probe_kernel_{read,write}(), | ||
154 | * which can be invoked from places like kgdb, /dev/mem (for reading) | ||
155 | * and privileged BPF code (for reading). | ||
156 | * The probe_kernel_*() functions set the kernel_uaccess_faults_ok flag | ||
157 | * to tell us that faulting on kernel addresses, and even noncanonical | ||
158 | * addresses, in a userspace accessor does not necessarily imply a | ||
159 | * kernel bug, root might just be doing weird stuff. | ||
160 | */ | ||
161 | if (current->kernel_uaccess_faults_ok) | ||
162 | return false; | ||
163 | |||
164 | /* This is bad. Refuse the fixup so that we go into die(). */ | ||
165 | if (trapnr == X86_TRAP_PF) { | ||
166 | pr_emerg("BUG: pagefault on kernel address 0x%lx in non-whitelisted uaccess\n", | ||
167 | fault_addr); | ||
168 | } else { | ||
169 | pr_emerg("BUG: GPF in non-whitelisted uaccess (non-canonical address?)\n"); | ||
170 | } | ||
171 | return true; | ||
172 | } | ||
173 | |||
174 | __visible bool ex_handler_uaccess(const struct exception_table_entry *fixup, | ||
175 | struct pt_regs *regs, int trapnr, | ||
176 | unsigned long error_code, | ||
177 | unsigned long fault_addr) | ||
178 | { | ||
179 | if (bogus_uaccess(regs, trapnr, fault_addr)) | ||
180 | return false; | ||
181 | regs->ip = ex_fixup_addr(fixup); | ||
182 | return true; | ||
183 | } | ||
184 | EXPORT_SYMBOL(ex_handler_uaccess); | ||
185 | |||
111 | __visible bool ex_handler_ext(const struct exception_table_entry *fixup, | 186 | __visible bool ex_handler_ext(const struct exception_table_entry *fixup, |
112 | struct pt_regs *regs, int trapnr) | 187 | struct pt_regs *regs, int trapnr, |
188 | unsigned long error_code, | ||
189 | unsigned long fault_addr) | ||
113 | { | 190 | { |
191 | if (bogus_uaccess(regs, trapnr, fault_addr)) | ||
192 | return false; | ||
114 | /* Special hack for uaccess_err */ | 193 | /* Special hack for uaccess_err */ |
115 | current->thread.uaccess_err = 1; | 194 | current->thread.uaccess_err = 1; |
116 | regs->ip = ex_fixup_addr(fixup); | 195 | regs->ip = ex_fixup_addr(fixup); |
@@ -119,7 +198,9 @@ __visible bool ex_handler_ext(const struct exception_table_entry *fixup, | |||
119 | EXPORT_SYMBOL(ex_handler_ext); | 198 | EXPORT_SYMBOL(ex_handler_ext); |
120 | 199 | ||
121 | __visible bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup, | 200 | __visible bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup, |
122 | struct pt_regs *regs, int trapnr) | 201 | struct pt_regs *regs, int trapnr, |
202 | unsigned long error_code, | ||
203 | unsigned long fault_addr) | ||
123 | { | 204 | { |
124 | if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pF)\n", | 205 | if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pF)\n", |
125 | (unsigned int)regs->cx, regs->ip, (void *)regs->ip)) | 206 | (unsigned int)regs->cx, regs->ip, (void *)regs->ip)) |
@@ -134,7 +215,9 @@ __visible bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup | |||
134 | EXPORT_SYMBOL(ex_handler_rdmsr_unsafe); | 215 | EXPORT_SYMBOL(ex_handler_rdmsr_unsafe); |
135 | 216 | ||
136 | __visible bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup, | 217 | __visible bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup, |
137 | struct pt_regs *regs, int trapnr) | 218 | struct pt_regs *regs, int trapnr, |
219 | unsigned long error_code, | ||
220 | unsigned long fault_addr) | ||
138 | { | 221 | { |
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", | 222 | if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pF)\n", |
140 | (unsigned int)regs->cx, (unsigned int)regs->dx, | 223 | (unsigned int)regs->cx, (unsigned int)regs->dx, |
@@ -148,12 +231,14 @@ __visible bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup | |||
148 | EXPORT_SYMBOL(ex_handler_wrmsr_unsafe); | 231 | EXPORT_SYMBOL(ex_handler_wrmsr_unsafe); |
149 | 232 | ||
150 | __visible bool ex_handler_clear_fs(const struct exception_table_entry *fixup, | 233 | __visible bool ex_handler_clear_fs(const struct exception_table_entry *fixup, |
151 | struct pt_regs *regs, int trapnr) | 234 | struct pt_regs *regs, int trapnr, |
235 | unsigned long error_code, | ||
236 | unsigned long fault_addr) | ||
152 | { | 237 | { |
153 | if (static_cpu_has(X86_BUG_NULL_SEG)) | 238 | if (static_cpu_has(X86_BUG_NULL_SEG)) |
154 | asm volatile ("mov %0, %%fs" : : "rm" (__USER_DS)); | 239 | asm volatile ("mov %0, %%fs" : : "rm" (__USER_DS)); |
155 | asm volatile ("mov %0, %%fs" : : "rm" (0)); | 240 | asm volatile ("mov %0, %%fs" : : "rm" (0)); |
156 | return ex_handler_default(fixup, regs, trapnr); | 241 | return ex_handler_default(fixup, regs, trapnr, error_code, fault_addr); |
157 | } | 242 | } |
158 | EXPORT_SYMBOL(ex_handler_clear_fs); | 243 | EXPORT_SYMBOL(ex_handler_clear_fs); |
159 | 244 | ||
@@ -170,7 +255,8 @@ __visible bool ex_has_fault_handler(unsigned long ip) | |||
170 | return handler == ex_handler_fault; | 255 | return handler == ex_handler_fault; |
171 | } | 256 | } |
172 | 257 | ||
173 | int fixup_exception(struct pt_regs *regs, int trapnr) | 258 | int fixup_exception(struct pt_regs *regs, int trapnr, unsigned long error_code, |
259 | unsigned long fault_addr) | ||
174 | { | 260 | { |
175 | const struct exception_table_entry *e; | 261 | const struct exception_table_entry *e; |
176 | ex_handler_t handler; | 262 | ex_handler_t handler; |
@@ -194,7 +280,7 @@ int fixup_exception(struct pt_regs *regs, int trapnr) | |||
194 | return 0; | 280 | return 0; |
195 | 281 | ||
196 | handler = ex_fixup_handler(e); | 282 | handler = ex_fixup_handler(e); |
197 | return handler(e, regs, trapnr); | 283 | return handler(e, regs, trapnr, error_code, fault_addr); |
198 | } | 284 | } |
199 | 285 | ||
200 | extern unsigned int early_recursion_flag; | 286 | extern unsigned int early_recursion_flag; |
@@ -230,9 +316,9 @@ void __init early_fixup_exception(struct pt_regs *regs, int trapnr) | |||
230 | * result in a hard-to-debug panic. | 316 | * result in a hard-to-debug panic. |
231 | * | 317 | * |
232 | * Keep in mind that not all vectors actually get here. Early | 318 | * Keep in mind that not all vectors actually get here. Early |
233 | * fage faults, for example, are special. | 319 | * page faults, for example, are special. |
234 | */ | 320 | */ |
235 | if (fixup_exception(regs, trapnr)) | 321 | if (fixup_exception(regs, trapnr, regs->orig_ax, 0)) |
236 | return; | 322 | return; |
237 | 323 | ||
238 | if (fixup_bug(regs, trapnr)) | 324 | if (fixup_bug(regs, trapnr)) |