diff options
author | Jeremy Fitzhardinge <jeremy@goop.org> | 2008-01-30 07:34:11 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:34:11 -0500 |
commit | 5b727a3b0158a129827c21ce3bfb0ba997e8ddd0 (patch) | |
tree | e0d43e258fa46e22ac6c4daf82c5005dc0e762c6 /arch/x86/mm/fault.c | |
parent | b406ac61e94875723540bd56e26f634afdeef489 (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/x86/mm/fault.c')
-rw-r--r-- | arch/x86/mm/fault.c | 55 |
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 | */ | ||
446 | static 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. |