x86/mm, tracing: Fix CR2 corruption
Despite the current efforts to read CR2 before tracing happens there still exist a number of possible holes: idtentry page_fault do_page_fault has_error_code=1 call error_entry TRACE_IRQS_OFF call trace_hardirqs_off* #PF // modifies CR2 CALL_enter_from_user_mode __context_tracking_exit() trace_user_exit(0) #PF // modifies CR2 call do_page_fault address = read_cr2(); /* whoopsie */ And similar for i386. Fix it by pulling the CR2 read into the entry code, before any of that stuff gets a chance to run and ruin things. Reported-by: He Zhe <zhe.he@windriver.com> Reported-by: Eiichi Tsukata <devel@etsukata.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Andy Lutomirski <luto@kernel.org> Cc: bp@alien8.de Cc: rostedt@goodmis.org Cc: torvalds@linux-foundation.org Cc: hpa@zytor.com Cc: dave.hansen@linux.intel.com Cc: jgross@suse.com Cc: joel@joelfernandes.org Link: https://lkml.kernel.org/r/20190711114336.116812491@infradead.org Debugged-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:

committed by
Thomas Gleixner

parent
4234653e88
commit
a0d14b8909
@@ -1507,9 +1507,8 @@ good_area:
|
||||
NOKPROBE_SYMBOL(do_user_addr_fault);
|
||||
|
||||
/*
|
||||
* This routine handles page faults. It determines the address,
|
||||
* and the problem, and then passes it off to one of the appropriate
|
||||
* routines.
|
||||
* Explicitly marked noinline such that the function tracer sees this as the
|
||||
* page_fault entry point.
|
||||
*/
|
||||
static noinline void
|
||||
__do_page_fault(struct pt_regs *regs, unsigned long hw_error_code,
|
||||
@@ -1528,33 +1527,26 @@ __do_page_fault(struct pt_regs *regs, unsigned long hw_error_code,
|
||||
}
|
||||
NOKPROBE_SYMBOL(__do_page_fault);
|
||||
|
||||
static nokprobe_inline void
|
||||
trace_page_fault_entries(unsigned long address, struct pt_regs *regs,
|
||||
unsigned long error_code)
|
||||
static __always_inline void
|
||||
trace_page_fault_entries(struct pt_regs *regs, unsigned long error_code,
|
||||
unsigned long address)
|
||||
{
|
||||
if (!trace_pagefault_enabled())
|
||||
return;
|
||||
|
||||
if (user_mode(regs))
|
||||
trace_page_fault_user(address, regs, error_code);
|
||||
else
|
||||
trace_page_fault_kernel(address, regs, error_code);
|
||||
}
|
||||
|
||||
/*
|
||||
* We must have this function blacklisted from kprobes, tagged with notrace
|
||||
* and call read_cr2() before calling anything else. To avoid calling any
|
||||
* kind of tracing machinery before we've observed the CR2 value.
|
||||
*
|
||||
* exception_{enter,exit}() contains all sorts of tracepoints.
|
||||
*/
|
||||
dotraplinkage void notrace
|
||||
do_page_fault(struct pt_regs *regs, unsigned long error_code)
|
||||
dotraplinkage void
|
||||
do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address)
|
||||
{
|
||||
unsigned long address = read_cr2(); /* Get the faulting address */
|
||||
enum ctx_state prev_state;
|
||||
|
||||
prev_state = exception_enter();
|
||||
if (trace_pagefault_enabled())
|
||||
trace_page_fault_entries(address, regs, error_code);
|
||||
|
||||
trace_page_fault_entries(regs, error_code, address);
|
||||
__do_page_fault(regs, error_code, address);
|
||||
exception_exit(prev_state);
|
||||
}
|
||||
|
Reference in New Issue
Block a user