aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJeremy Fitzhardinge <jeremy@goop.org>2008-01-30 07:34:11 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:34:11 -0500
commit5b727a3b0158a129827c21ce3bfb0ba997e8ddd0 (patch)
treee0d43e258fa46e22ac6c4daf82c5005dc0e762c6 /arch
parentb406ac61e94875723540bd56e26f634afdeef489 (diff)
x86: ignore spurious faults
When changing a kernel page from RO->RW, it's OK to leave stale TLB entries around, since doing a global flush is expensive and they pose no security problem. They can, however, generate a spurious fault, which we should catch and simply return from (which will have the side-effect of reloading the TLB to the current PTE). This can occur when running under Xen, because it frequently changes kernel pages from RW->RO->RW to implement Xen's pagetable semantics. It could also occur when using CONFIG_DEBUG_PAGEALLOC, since it avoids doing a global TLB flush after changing page permissions. Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com> Cc: Harvey Harrison <harvey.harrison@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/mm/fault.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 99d273dbc758..1c836527dde7 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -435,6 +435,51 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
435#endif 435#endif
436 436
437/* 437/*
438 * Handle a spurious fault caused by a stale TLB entry. This allows
439 * us to lazily refresh the TLB when increasing the permissions of a
440 * kernel page (RO -> RW or NX -> X). Doing it eagerly is very
441 * expensive since that implies doing a full cross-processor TLB
442 * flush, even if no stale TLB entries exist on other processors.
443 * There are no security implications to leaving a stale TLB when
444 * increasing the permissions on a page.
445 */
446static int spurious_fault(unsigned long address,
447 unsigned long error_code)
448{
449 pgd_t *pgd;
450 pud_t *pud;
451 pmd_t *pmd;
452 pte_t *pte;
453
454 /* Reserved-bit violation or user access to kernel space? */
455 if (error_code & (PF_USER | PF_RSVD))
456 return 0;
457
458 pgd = init_mm.pgd + pgd_index(address);
459 if (!pgd_present(*pgd))
460 return 0;
461
462 pud = pud_offset(pgd, address);
463 if (!pud_present(*pud))
464 return 0;
465
466 pmd = pmd_offset(pud, address);
467 if (!pmd_present(*pmd))
468 return 0;
469
470 pte = pte_offset_kernel(pmd, address);
471 if (!pte_present(*pte))
472 return 0;
473
474 if ((error_code & PF_WRITE) && !pte_write(*pte))
475 return 0;
476 if ((error_code & PF_INSTR) && !pte_exec(*pte))
477 return 0;
478
479 return 1;
480}
481
482/*
438 * X86_32 483 * X86_32
439 * Handle a fault on the vmalloc or module mapping area 484 * Handle a fault on the vmalloc or module mapping area
440 * 485 *
@@ -568,6 +613,11 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
568 if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) && 613 if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) &&
569 vmalloc_fault(address) >= 0) 614 vmalloc_fault(address) >= 0)
570 return; 615 return;
616
617 /* Can handle a stale RO->RW TLB */
618 if (spurious_fault(address, error_code))
619 return;
620
571 /* 621 /*
572 * Don't take the mm semaphore here. If we fixup a prefetch 622 * Don't take the mm semaphore here. If we fixup a prefetch
573 * fault we could otherwise deadlock. 623 * fault we could otherwise deadlock.
@@ -598,6 +648,11 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
598 if (vmalloc_fault(address) >= 0) 648 if (vmalloc_fault(address) >= 0)
599 return; 649 return;
600 } 650 }
651
652 /* Can handle a stale RO->RW TLB */
653 if (spurious_fault(address, error_code))
654 return;
655
601 /* 656 /*
602 * Don't take the mm semaphore here. If we fixup a prefetch 657 * Don't take the mm semaphore here. If we fixup a prefetch
603 * fault we could otherwise deadlock. 658 * fault we could otherwise deadlock.