unsigned char *max_instr;
#ifdef CONFIG_X86_32
-# ifdef CONFIG_X86_PAE
- /* If it was a exec fault on NX page, ignore */
- if (nx_enabled && (error_code & PF_INSTR))
+ if (!(__supported_pte_mask & _PAGE_NX))
return 0;
-# else
- return 0;
-# endif
-#else /* CONFIG_X86_64 */
+#endif
+
/* If it was a exec fault on NX page, ignore */
if (error_code & PF_INSTR)
return 0;
-#endif
instr = (unsigned char *)convert_ip_to_linear(current, regs);
max_instr = instr + 15;
pud = pud_offset(pgd, address);
if (bad_address(pud)) goto bad;
printk("PUD %lx ", pud_val(*pud));
- if (!pud_present(*pud)) goto ret;
+ if (!pud_present(*pud) || pud_large(*pud))
+ goto ret;
pmd = pmd_offset(pud, address);
if (bad_address(pmd)) goto bad;
#ifdef CONFIG_X86_32
if (!oops_may_print())
return;
+#endif
#ifdef CONFIG_X86_PAE
if (error_code & PF_INSTR) {
- int level;
+ unsigned int level;
pte_t *pte = lookup_address(address, &level);
if (pte && pte_present(*pte) && !pte_exec(*pte))
"(uid: %d)\n", current->uid);
}
#endif
- printk(KERN_ALERT "BUG: unable to handle kernel ");
- if (address < PAGE_SIZE)
- printk(KERN_CONT "NULL pointer dereference");
- else
- printk(KERN_CONT "paging request");
- printk(KERN_CONT " at %08lx\n", address);
- printk(KERN_ALERT "IP:");
- printk_address(regs->ip, 1);
- dump_pagetable(address);
-#else /* CONFIG_X86_64 */
printk(KERN_ALERT "BUG: unable to handle kernel ");
if (address < PAGE_SIZE)
printk(KERN_CONT "NULL pointer dereference");
else
printk(KERN_CONT "paging request");
+#ifdef CONFIG_X86_32
+ printk(KERN_CONT " at %08lx\n", address);
+#else
printk(KERN_CONT " at %016lx\n", address);
-
+#endif
printk(KERN_ALERT "IP:");
printk_address(regs->ip, 1);
dump_pagetable(address);
-#endif
}
#ifdef CONFIG_X86_64
}
#endif
+/*
+ * Handle a spurious fault caused by a stale TLB entry. This allows
+ * us to lazily refresh the TLB when increasing the permissions of a
+ * kernel page (RO -> RW or NX -> X). Doing it eagerly is very
+ * expensive since that implies doing a full cross-processor TLB
+ * flush, even if no stale TLB entries exist on other processors.
+ * There are no security implications to leaving a stale TLB when
+ * increasing the permissions on a page.
+ */
+static int spurious_fault(unsigned long address,
+ unsigned long error_code)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /* Reserved-bit violation or user access to kernel space? */
+ if (error_code & (PF_USER | PF_RSVD))
+ return 0;
+
+ pgd = init_mm.pgd + pgd_index(address);
+ if (!pgd_present(*pgd))
+ return 0;
+
+ pud = pud_offset(pgd, address);
+ if (!pud_present(*pud))
+ return 0;
+
+ pmd = pmd_offset(pud, address);
+ if (!pmd_present(*pmd))
+ return 0;
+
+ pte = pte_offset_kernel(pmd, address);
+ if (!pte_present(*pte))
+ return 0;
+
+ if ((error_code & PF_WRITE) && !pte_write(*pte))
+ return 0;
+ if ((error_code & PF_INSTR) && !pte_exec(*pte))
+ return 0;
+
+ return 1;
+}
+
/*
* X86_32
* Handle a fault on the vmalloc or module mapping area
pmd_t *pmd, *pmd_ref;
pte_t *pte, *pte_ref;
+ /* Make sure we are in vmalloc area */
+ if (!(address >= VMALLOC_START && address < VMALLOC_END))
+ return -1;
+
/* Copy kernel mappings over when needed. This can also
happen within a race in page table update. In the later
case just flush. */
*/
#ifdef CONFIG_X86_32
if (unlikely(address >= TASK_SIZE)) {
+#else
+ if (unlikely(address >= TASK_SIZE64)) {
+#endif
if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) &&
vmalloc_fault(address) >= 0)
return;
+
+ /* Can handle a stale RO->RW TLB */
+ if (spurious_fault(address, error_code))
+ return;
+
/*
* Don't take the mm semaphore here. If we fixup a prefetch
* fault we could otherwise deadlock.
goto bad_area_nosemaphore;
}
+
+#ifdef CONFIG_X86_32
/* It's safe to allow irq's after cr2 has been saved and the vmalloc
fault has been handled. */
if (regs->flags & (X86_EFLAGS_IF|VM_MASK))
if (in_atomic() || !mm)
goto bad_area_nosemaphore;
#else /* CONFIG_X86_64 */
- if (unlikely(address >= TASK_SIZE64)) {
- /*
- * Don't check for the module range here: its PML4
- * is always initialized because it's shared with the main
- * kernel text. Only vmalloc may need PML4 syncups.
- */
- if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) &&
- ((address >= VMALLOC_START && address < VMALLOC_END))) {
- if (vmalloc_fault(address) >= 0)
- return;
- }
- /*
- * Don't take the mm semaphore here. If we fixup a prefetch
- * fault we could otherwise deadlock.
- */
- goto bad_area_nosemaphore;
- }
if (likely(regs->flags & X86_EFLAGS_IF))
local_irq_enable();
vma = find_vma(mm, address);
if (!vma)
goto bad_area;
-#ifdef CONFIG_X86_32
if (vma->vm_start <= address)
-#else
- if (likely(vma->vm_start <= address))
-#endif
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
*/
#ifdef CONFIG_X86_32
bust_spinlocks(1);
+#else
+ flags = oops_begin();
+#endif
show_fault_oops(regs, error_code, address);
tsk->thread.cr2 = address;
tsk->thread.trap_no = 14;
tsk->thread.error_code = error_code;
+
+#ifdef CONFIG_X86_32
die("Oops", regs, error_code);
bust_spinlocks(0);
do_exit(SIGKILL);
-#else /* CONFIG_X86_64 */
- flags = oops_begin();
-
- show_fault_oops(regs, error_code, address);
-
- tsk->thread.cr2 = address;
- tsk->thread.trap_no = 14;
- tsk->thread.error_code = error_code;
+#else
if (__die("Oops", regs, error_code))
regs = NULL;
/* Executive summary in case the body of the oops scrolled away */
*/
out_of_memory:
up_read(&mm->mmap_sem);
-#ifdef CONFIG_X86_32
if (is_global_init(tsk)) {
yield();
+#ifdef CONFIG_X86_32
down_read(&mm->mmap_sem);
goto survive;
- }
#else
- if (is_global_init(current)) {
- yield();
goto again;
- }
#endif
+ }
+
printk("VM: killing process %s\n", tsk->comm);
if (error_code & PF_USER)
do_group_exit(SIGKILL);
force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk);
}
-#ifdef CONFIG_X86_64
DEFINE_SPINLOCK(pgd_lock);
LIST_HEAD(pgd_list);
-#endif
void vmalloc_sync_all(void)
{
struct page *page;
spin_lock_irqsave(&pgd_lock, flags);
- for (page = pgd_list; page; page =
- (struct page *)page->index)
+ list_for_each_entry(page, &pgd_list, lru) {
if (!vmalloc_sync_one(page_address(page),
- address)) {
- BUG_ON(page != pgd_list);
+ address))
break;
- }
+ }
spin_unlock_irqrestore(&pgd_lock, flags);
if (!page)
set_bit(pgd_index(address), insync);