Commit ebc8827f authored by Frederic Weisbecker's avatar Frederic Weisbecker

x86: Barf when vmalloc and kmemcheck faults happen in NMI

In x86, faults exit by executing the iret instruction, which then
reenables NMIs if we faulted in NMI context. Then if a fault
happens in NMI, another NMI can nest after the fault exits.

But we don't yet support nested NMIs because we have only one NMI
stack. To prevent from that, check that vmalloc and kmemcheck
faults don't happen in this context. Most of the other kernel faults
in NMIs can be more easily spotted by finding explicit
copy_from,to_user() calls on review.
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
parent 14cae9bd
...@@ -251,6 +251,8 @@ static noinline __kprobes int vmalloc_fault(unsigned long address) ...@@ -251,6 +251,8 @@ static noinline __kprobes int vmalloc_fault(unsigned long address)
if (!(address >= VMALLOC_START && address < VMALLOC_END)) if (!(address >= VMALLOC_START && address < VMALLOC_END))
return -1; return -1;
WARN_ON_ONCE(in_nmi());
/* /*
* Synchronize this task's top level page-table * Synchronize this task's top level page-table
* with the 'reference' page table. * with the 'reference' page table.
...@@ -369,6 +371,8 @@ static noinline __kprobes int vmalloc_fault(unsigned long address) ...@@ -369,6 +371,8 @@ static noinline __kprobes int vmalloc_fault(unsigned long address)
if (!(address >= VMALLOC_START && address < VMALLOC_END)) if (!(address >= VMALLOC_START && address < VMALLOC_END))
return -1; return -1;
WARN_ON_ONCE(in_nmi());
/* /*
* Copy kernel mappings over when needed. This can also * Copy kernel mappings over when needed. This can also
* happen within a race in page table update. In the later * happen within a race in page table update. In the later
......
...@@ -631,6 +631,8 @@ bool kmemcheck_fault(struct pt_regs *regs, unsigned long address, ...@@ -631,6 +631,8 @@ bool kmemcheck_fault(struct pt_regs *regs, unsigned long address,
if (!pte) if (!pte)
return false; return false;
WARN_ON_ONCE(in_nmi());
if (error_code & 2) if (error_code & 2)
kmemcheck_access(regs, address, KMEMCHECK_WRITE); kmemcheck_access(regs, address, KMEMCHECK_WRITE);
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment