diff options
Diffstat (limited to 'arch/x86/mm/fault.c')
-rw-r--r-- | arch/x86/mm/fault.c | 54 |
1 files changed, 39 insertions, 15 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 6dea040cc3a1..8e5722992677 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -584,8 +584,13 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code, | |||
584 | 584 | ||
585 | if (error_code & PF_INSTR) { | 585 | if (error_code & PF_INSTR) { |
586 | unsigned int level; | 586 | unsigned int level; |
587 | pgd_t *pgd; | ||
588 | pte_t *pte; | ||
587 | 589 | ||
588 | pte_t *pte = lookup_address(address, &level); | 590 | pgd = __va(read_cr3() & PHYSICAL_PAGE_MASK); |
591 | pgd += pgd_index(address); | ||
592 | |||
593 | pte = lookup_address_in_pgd(pgd, address, &level); | ||
589 | 594 | ||
590 | if (pte && pte_present(*pte) && !pte_exec(*pte)) | 595 | if (pte && pte_present(*pte) && !pte_exec(*pte)) |
591 | printk(nx_warning, from_kuid(&init_user_ns, current_uid())); | 596 | printk(nx_warning, from_kuid(&init_user_ns, current_uid())); |
@@ -1020,13 +1025,17 @@ static inline bool smap_violation(int error_code, struct pt_regs *regs) | |||
1020 | * This routine handles page faults. It determines the address, | 1025 | * This routine handles page faults. It determines the address, |
1021 | * and the problem, and then passes it off to one of the appropriate | 1026 | * and the problem, and then passes it off to one of the appropriate |
1022 | * routines. | 1027 | * routines. |
1028 | * | ||
1029 | * This function must have noinline because both callers | ||
1030 | * {,trace_}do_page_fault() have notrace on. Having this an actual function | ||
1031 | * guarantees there's a function trace entry. | ||
1023 | */ | 1032 | */ |
1024 | static void __kprobes | 1033 | static void __kprobes noinline |
1025 | __do_page_fault(struct pt_regs *regs, unsigned long error_code) | 1034 | __do_page_fault(struct pt_regs *regs, unsigned long error_code, |
1035 | unsigned long address) | ||
1026 | { | 1036 | { |
1027 | struct vm_area_struct *vma; | 1037 | struct vm_area_struct *vma; |
1028 | struct task_struct *tsk; | 1038 | struct task_struct *tsk; |
1029 | unsigned long address; | ||
1030 | struct mm_struct *mm; | 1039 | struct mm_struct *mm; |
1031 | int fault; | 1040 | int fault; |
1032 | unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; | 1041 | unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; |
@@ -1034,9 +1043,6 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
1034 | tsk = current; | 1043 | tsk = current; |
1035 | mm = tsk->mm; | 1044 | mm = tsk->mm; |
1036 | 1045 | ||
1037 | /* Get the faulting address: */ | ||
1038 | address = read_cr2(); | ||
1039 | |||
1040 | /* | 1046 | /* |
1041 | * Detect and handle instructions that would cause a page fault for | 1047 | * Detect and handle instructions that would cause a page fault for |
1042 | * both a tracked kernel page and a userspace page. | 1048 | * both a tracked kernel page and a userspace page. |
@@ -1248,32 +1254,50 @@ good_area: | |||
1248 | up_read(&mm->mmap_sem); | 1254 | up_read(&mm->mmap_sem); |
1249 | } | 1255 | } |
1250 | 1256 | ||
1251 | dotraplinkage void __kprobes | 1257 | dotraplinkage void __kprobes notrace |
1252 | do_page_fault(struct pt_regs *regs, unsigned long error_code) | 1258 | do_page_fault(struct pt_regs *regs, unsigned long error_code) |
1253 | { | 1259 | { |
1260 | unsigned long address = read_cr2(); /* Get the faulting address */ | ||
1254 | enum ctx_state prev_state; | 1261 | enum ctx_state prev_state; |
1255 | 1262 | ||
1263 | /* | ||
1264 | * We must have this function tagged with __kprobes, notrace and call | ||
1265 | * read_cr2() before calling anything else. To avoid calling any kind | ||
1266 | * of tracing machinery before we've observed the CR2 value. | ||
1267 | * | ||
1268 | * exception_{enter,exit}() contain all sorts of tracepoints. | ||
1269 | */ | ||
1270 | |||
1256 | prev_state = exception_enter(); | 1271 | prev_state = exception_enter(); |
1257 | __do_page_fault(regs, error_code); | 1272 | __do_page_fault(regs, error_code, address); |
1258 | exception_exit(prev_state); | 1273 | exception_exit(prev_state); |
1259 | } | 1274 | } |
1260 | 1275 | ||
1261 | static void trace_page_fault_entries(struct pt_regs *regs, | 1276 | #ifdef CONFIG_TRACING |
1277 | static void trace_page_fault_entries(unsigned long address, struct pt_regs *regs, | ||
1262 | unsigned long error_code) | 1278 | unsigned long error_code) |
1263 | { | 1279 | { |
1264 | if (user_mode(regs)) | 1280 | if (user_mode(regs)) |
1265 | trace_page_fault_user(read_cr2(), regs, error_code); | 1281 | trace_page_fault_user(address, regs, error_code); |
1266 | else | 1282 | else |
1267 | trace_page_fault_kernel(read_cr2(), regs, error_code); | 1283 | trace_page_fault_kernel(address, regs, error_code); |
1268 | } | 1284 | } |
1269 | 1285 | ||
1270 | dotraplinkage void __kprobes | 1286 | dotraplinkage void __kprobes notrace |
1271 | trace_do_page_fault(struct pt_regs *regs, unsigned long error_code) | 1287 | trace_do_page_fault(struct pt_regs *regs, unsigned long error_code) |
1272 | { | 1288 | { |
1289 | /* | ||
1290 | * The exception_enter and tracepoint processing could | ||
1291 | * trigger another page faults (user space callchain | ||
1292 | * reading) and destroy the original cr2 value, so read | ||
1293 | * the faulting address now. | ||
1294 | */ | ||
1295 | unsigned long address = read_cr2(); | ||
1273 | enum ctx_state prev_state; | 1296 | enum ctx_state prev_state; |
1274 | 1297 | ||
1275 | prev_state = exception_enter(); | 1298 | prev_state = exception_enter(); |
1276 | trace_page_fault_entries(regs, error_code); | 1299 | trace_page_fault_entries(address, regs, error_code); |
1277 | __do_page_fault(regs, error_code); | 1300 | __do_page_fault(regs, error_code, address); |
1278 | exception_exit(prev_state); | 1301 | exception_exit(prev_state); |
1279 | } | 1302 | } |
1303 | #endif /* CONFIG_TRACING */ | ||