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.c25
1 files changed, 22 insertions, 3 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 463d1e9d026e..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{
@@ -224,7 +225,7 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr,
224 225
225/* Is this a bad kernel fault ? */ 226/* Is this a bad kernel fault ? */
226static bool bad_kernel_fault(struct pt_regs *regs, 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{
229 int is_exec = TRAP(regs) == 0x400; 230 int is_exec = TRAP(regs) == 0x400;
230 231
@@ -235,6 +236,9 @@ static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,
235 address >= TASK_SIZE ? "exec-protected" : "user", 236 address >= TASK_SIZE ? "exec-protected" : "user",
236 address, 237 address,
237 from_kuid(&init_user_ns, current_uid())); 238 from_kuid(&init_user_ns, current_uid()));
239
240 // Kernel exec fault is always bad
241 return true;
238 } 242 }
239 243
240 if (!is_exec && address < TASK_SIZE && (error_code & DSISR_PROTFAULT) && 244 if (!is_exec && address < TASK_SIZE && (error_code & DSISR_PROTFAULT) &&
@@ -244,7 +248,22 @@ static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,
244 from_kuid(&init_user_ns, current_uid())); 248 from_kuid(&init_user_ns, current_uid()));
245 } 249 }
246 250
247 return is_exec || (address >= TASK_SIZE) || !search_exception_tables(regs->nip); 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;
248} 267}
249 268
250static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address, 269static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address,
@@ -467,7 +486,7 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
467 * take a page fault to a kernel address or a page fault to a user 486 * take a page fault to a kernel address or a page fault to a user
468 * address outside of dedicated places 487 * address outside of dedicated places
469 */ 488 */
470 if (unlikely(!is_user && bad_kernel_fault(regs, error_code, address))) 489 if (unlikely(!is_user && bad_kernel_fault(regs, error_code, address, is_write)))
471 return SIGSEGV; 490 return SIGSEGV;
472 491
473 /* 492 /*