aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/mm/fault.c')
-rw-r--r--arch/powerpc/mm/fault.c49
1 files changed, 39 insertions, 10 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 887f11bcf330..b5d3578d9f65 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -44,6 +44,7 @@
44#include <asm/mmu_context.h> 44#include <asm/mmu_context.h>
45#include <asm/siginfo.h> 45#include <asm/siginfo.h>
46#include <asm/debug.h> 46#include <asm/debug.h>
47#include <asm/kup.h>
47 48
48static inline bool notify_page_fault(struct pt_regs *regs) 49static inline bool notify_page_fault(struct pt_regs *regs)
49{ 50{
@@ -223,19 +224,46 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr,
223} 224}
224 225
225/* Is this a bad kernel fault ? */ 226/* Is this a bad kernel fault ? */
226static bool bad_kernel_fault(bool is_exec, unsigned long error_code, 227static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,
227 unsigned long address) 228 unsigned long address, bool is_write)
228{ 229{
230 int is_exec = TRAP(regs) == 0x400;
231
229 /* NX faults set DSISR_PROTFAULT on the 8xx, DSISR_NOEXEC_OR_G on others */ 232 /* NX faults set DSISR_PROTFAULT on the 8xx, DSISR_NOEXEC_OR_G on others */
230 if (is_exec && (error_code & (DSISR_NOEXEC_OR_G | DSISR_KEYFAULT | 233 if (is_exec && (error_code & (DSISR_NOEXEC_OR_G | DSISR_KEYFAULT |
231 DSISR_PROTFAULT))) { 234 DSISR_PROTFAULT))) {
232 printk_ratelimited(KERN_CRIT "kernel tried to execute" 235 pr_crit_ratelimited("kernel tried to execute %s page (%lx) - exploit attempt? (uid: %d)\n",
233 " exec-protected page (%lx) -" 236 address >= TASK_SIZE ? "exec-protected" : "user",
234 "exploit attempt? (uid: %d)\n", 237 address,
235 address, from_kuid(&init_user_ns, 238 from_kuid(&init_user_ns, current_uid()));
236 current_uid())); 239
240 // Kernel exec fault is always bad
241 return true;
237 } 242 }
238 return is_exec || (address >= TASK_SIZE); 243
244 if (!is_exec && address < TASK_SIZE && (error_code & DSISR_PROTFAULT) &&
245 !search_exception_tables(regs->nip)) {
246 pr_crit_ratelimited("Kernel attempted to access user page (%lx) - exploit attempt? (uid: %d)\n",
247 address,
248 from_kuid(&init_user_ns, current_uid()));
249 }
250
251 // Kernel fault on kernel address is bad
252 if (address >= TASK_SIZE)
253 return true;
254
255 // Fault on user outside of certain regions (eg. copy_tofrom_user()) is bad
256 if (!search_exception_tables(regs->nip))
257 return true;
258
259 // Read/write fault in a valid region (the exception table search passed
260 // above), but blocked by KUAP is bad, it can never succeed.
261 if (bad_kuap_fault(regs, is_write))
262 return true;
263
264 // What's left? Kernel fault on user in well defined regions (extable
265 // matched), and allowed by KUAP in the faulting context.
266 return false;
239} 267}
240 268
241static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address, 269static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address,
@@ -455,9 +483,10 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
455 483
456 /* 484 /*
457 * The kernel should never take an execute fault nor should it 485 * The kernel should never take an execute fault nor should it
458 * take a page fault to a kernel address. 486 * take a page fault to a kernel address or a page fault to a user
487 * address outside of dedicated places
459 */ 488 */
460 if (unlikely(!is_user && bad_kernel_fault(is_exec, error_code, address))) 489 if (unlikely(!is_user && bad_kernel_fault(regs, error_code, address, is_write)))
461 return SIGSEGV; 490 return SIGSEGV;
462 491
463 /* 492 /*