Skip to content

Commit

Permalink
x86/fault: Split the OOPS code out from no_context()
Browse files Browse the repository at this point in the history
Not all callers of no_context() want to run exception fixups.
Separate the OOPS code out from the fixup code in no_context().

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/450f8d8eabafb83a5df349108c8e5ea83a2f939d.1612924255.git.luto@kernel.org
  • Loading branch information
Andy Lutomirski authored and Borislav Petkov committed Feb 10, 2021
1 parent 03c81ea commit 2cc624b
Showing 1 changed file with 62 additions and 54 deletions.
116 changes: 62 additions & 54 deletions arch/x86/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,62 +655,29 @@ static void set_signal_archinfo(unsigned long address,
}

static noinline void
no_context(struct pt_regs *regs, unsigned long error_code,
unsigned long address, int signal, int si_code)
page_fault_oops(struct pt_regs *regs, unsigned long error_code,
unsigned long address)
{
struct task_struct *tsk = current;
unsigned long flags;
int sig;

if (user_mode(regs)) {
/*
* This is an implicit supervisor-mode access from user
* mode. Bypass all the kernel-mode recovery code and just
* OOPS.
* Implicit kernel access from user mode? Skip the stack
* overflow and EFI special cases.
*/
goto oops;
}

/* Are we prepared to handle this kernel fault? */
if (fixup_exception(regs, X86_TRAP_PF, error_code, address)) {
/*
* Any interrupt that takes a fault gets the fixup. This makes
* the below recursive fault logic only apply to a faults from
* task context.
*/
if (in_interrupt())
return;

/*
* Per the above we're !in_interrupt(), aka. task context.
*
* In this case we need to make sure we're not recursively
* faulting through the emulate_vsyscall() logic.
*/
if (current->thread.sig_on_uaccess_err && signal) {
sanitize_error_code(address, &error_code);

set_signal_archinfo(address, error_code);

/* XXX: hwpoison faults will set the wrong code. */
force_sig_fault(signal, si_code, (void __user *)address);
}

/*
* Barring that, we can do the fixup and be happy.
*/
return;
}

#ifdef CONFIG_VMAP_STACK
/*
* Stack overflow? During boot, we can fault near the initial
* stack in the direct map, but that's not an overflow -- check
* that we're in vmalloc space to avoid this.
*/
if (is_vmalloc_addr((void *)address) &&
(((unsigned long)tsk->stack - 1 - address < PAGE_SIZE) ||
address - ((unsigned long)tsk->stack + THREAD_SIZE) < PAGE_SIZE)) {
(((unsigned long)current->stack - 1 - address < PAGE_SIZE) ||
address - ((unsigned long)current->stack + THREAD_SIZE) < PAGE_SIZE)) {
unsigned long stack = __this_cpu_ist_top_va(DF) - sizeof(void *);
/*
* We're likely to be running with very little stack space
Expand All @@ -733,20 +700,6 @@ no_context(struct pt_regs *regs, unsigned long error_code,
}
#endif

/*
* 32-bit:
*
* Valid to do another page fault here, because if this fault
* had been triggered by is_prefetch fixup_exception would have
* handled it.
*
* 64-bit:
*
* Hall of shame of CPU/BIOS bugs.
*/
if (is_prefetch(regs, error_code, address))
return;

/*
* Buggy firmware could access regions which might page fault, try to
* recover from such faults.
Expand All @@ -763,7 +716,7 @@ no_context(struct pt_regs *regs, unsigned long error_code,

show_fault_oops(regs, error_code, address);

if (task_stack_end_corrupted(tsk))
if (task_stack_end_corrupted(current))
printk(KERN_EMERG "Thread overran stack, or stack corrupted\n");

sig = SIGKILL;
Expand All @@ -776,6 +729,61 @@ no_context(struct pt_regs *regs, unsigned long error_code,
oops_end(flags, regs, sig);
}

static noinline void
no_context(struct pt_regs *regs, unsigned long error_code,
unsigned long address, int signal, int si_code)
{
if (user_mode(regs)) {
/*
* This is an implicit supervisor-mode access from user
* mode. Bypass all the kernel-mode recovery code and just
* OOPS.
*/
goto oops;
}

/* Are we prepared to handle this kernel fault? */
if (fixup_exception(regs, X86_TRAP_PF, error_code, address)) {
/*
* Any interrupt that takes a fault gets the fixup. This makes
* the below recursive fault logic only apply to a faults from
* task context.
*/
if (in_interrupt())
return;

/*
* Per the above we're !in_interrupt(), aka. task context.
*
* In this case we need to make sure we're not recursively
* faulting through the emulate_vsyscall() logic.
*/
if (current->thread.sig_on_uaccess_err && signal) {
sanitize_error_code(address, &error_code);

set_signal_archinfo(address, error_code);

/* XXX: hwpoison faults will set the wrong code. */
force_sig_fault(signal, si_code, (void __user *)address);
}

/*
* Barring that, we can do the fixup and be happy.
*/
return;
}

/*
* AMD erratum #91 manifests as a spurious page fault on a PREFETCH
* instruction.
*/
if (is_prefetch(regs, error_code, address))
return;

oops:
page_fault_oops(regs, error_code, address);
}

/*
* Print out info about fatal segfaults, if the show_unhandled_signals
* sysctl is set:
Expand Down

0 comments on commit 2cc624b

Please sign in to comment.