From 8b4431ffa91210cdae833ec7517aef2fca06e32e Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Wed, 14 Dec 2011 00:09:14 -0500 Subject: [PATCH] dtrace: support for page fault and general protection fault detection This ensures that DTrace memory access faults are non-fatal. Signed-off-by: Kris Van Hees --- arch/x86/mm/fault.c | 7 ++++- include/linux/dtrace_cpu.h | 58 ++++++++++++++++++++++++++++++++++++++ kernel/dtrace/Makefile | 4 +-- kernel/dtrace/dtrace_cpu.c | 12 ++++++++ kernel/dtrace/dtrace_os.c | 42 +++++++++++++++++++++++---- 5 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 include/linux/dtrace_cpu.h create mode 100644 kernel/dtrace/dtrace_cpu.c diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 181c53bac3a7..0c214252685c 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -701,8 +701,13 @@ no_context(struct pt_regs *regs, unsigned long error_code, /* * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice: + * terminate things with extreme prejudice, unless a notifier decides + * to let this one slide. */ + if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, + SIGKILL) == NOTIFY_STOP) + return; + flags = oops_begin(); show_fault_oops(regs, error_code, address); diff --git a/include/linux/dtrace_cpu.h b/include/linux/dtrace_cpu.h new file mode 100644 index 000000000000..37fd6d38454e --- /dev/null +++ b/include/linux/dtrace_cpu.h @@ -0,0 +1,58 @@ +#ifndef _DTRACE_CPU_H_ +#define _DTRACE_CPU_H_ + +#include +#include +#include + +#define CPUC_SIZE (sizeof (uint16_t) + sizeof(uint8_t) + \ + sizeof(uintptr_t) + sizeof(struct mutex)) +#define CPUC_PADSIZE (192 - CPUC_SIZE) + +typedef struct cpu_core { + uint16_t cpuc_dtrace_flags; + uint8_t cpuc_dcpc_intr_state; + uint8_t cpuc_pad[CPUC_PADSIZE]; + uintptr_t cpuc_dtrace_illval; + struct mutex cpuc_pid_lock; + + uintptr_t cpu_dtrace_caller; + ktime_t cpu_dtrace_chillmark; + ktime_t cpu_dtrace_chilled; +} cpu_core_t; + +DECLARE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info); + +#define per_cpu_core(cpu) (&per_cpu(dtrace_cpu_info, (cpu))) +#define this_cpu_core (&__get_cpu_var(dtrace_cpu_info)) + +#define DTRACE_CPUFLAG_ISSET(flag) \ + (this_cpu_core->cpuc_dtrace_flags & (flag)) + +#define DTRACE_CPUFLAG_SET(flag) \ + (this_cpu_core->cpuc_dtrace_flags |= (flag)) + +#define DTRACE_CPUFLAG_CLEAR(flag) \ + (this_cpu_core->cpuc_dtrace_flags &= ~(flag)) + +#define CPU_DTRACE_NOFAULT 0x0001 +#define CPU_DTRACE_DROP 0x0002 +#define CPU_DTRACE_BADADDR 0x0004 +#define CPU_DTRACE_BADALIGN 0x0008 +#define CPU_DTRACE_DIVZERO 0x0010 +#define CPU_DTRACE_ILLOP 0x0020 +#define CPU_DTRACE_NOSCRATCH 0x0040 +#define CPU_DTRACE_KPRIV 0x0080 +#define CPU_DTRACE_UPRIV 0x0100 +#define CPU_DTRACE_TUPOFLOW 0x0200 +#define CPU_DTRACE_ENTRY 0x0800 +#define CPU_DTRACE_BADSTACK 0x1000 + +#define CPU_DTRACE_FAULT (CPU_DTRACE_BADADDR | CPU_DTRACE_BADALIGN | \ + CPU_DTRACE_DIVZERO | CPU_DTRACE_ILLOP | \ + CPU_DTRACE_NOSCRATCH | CPU_DTRACE_KPRIV | \ + CPU_DTRACE_UPRIV | CPU_DTRACE_TUPOFLOW | \ + CPU_DTRACE_BADSTACK) +#define CPU_DTRACE_ERROR (CPU_DTRACE_FAULT | CPU_DTRACE_DROP) + +#endif /* _DTRACE_CPU_H_ */ diff --git a/kernel/dtrace/Makefile b/kernel/dtrace/Makefile index 1713210d969e..957a01cef24f 100644 --- a/kernel/dtrace/Makefile +++ b/kernel/dtrace/Makefile @@ -5,6 +5,6 @@ GCOV_PROFILE := y ifdef CONFIG_DT_CORE -obj-y += dtrace_os.o dtrace_stubs_x86_64.o \ - sdt_register.o +obj-y += dtrace_os.o dtrace_cpu.o \ + dtrace_stubs_x86_64.o sdt_register.o endif diff --git a/kernel/dtrace/dtrace_cpu.c b/kernel/dtrace/dtrace_cpu.c new file mode 100644 index 000000000000..4b1d249c70dc --- /dev/null +++ b/kernel/dtrace/dtrace_cpu.c @@ -0,0 +1,12 @@ +/* + * FILE: dtrace_cpu.c + * DESCRIPTION: Dynamic Tracing: CPU info - part of kernel core + * + * Copyright (C) 2010, 2011 Oracle Corporation + */ + +#include +#include + +DEFINE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info); +EXPORT_PER_CPU_SYMBOL(dtrace_cpu_info); diff --git a/kernel/dtrace/dtrace_os.c b/kernel/dtrace/dtrace_os.c index 2851e1528337..821a6f610512 100644 --- a/kernel/dtrace/dtrace_os.c +++ b/kernel/dtrace/dtrace_os.c @@ -6,16 +6,17 @@ */ #include +#include #include +#include #include #include #include +#include #include #include +#include #include - -#include -#include #include /* @@ -248,10 +249,42 @@ static int dtrace_die_notifier(struct notifier_block *nb, unsigned long val, dtrace_invop_hdlr_t *hdlr; int rval = 0; + if (val == DIE_PAGE_FAULT) { + unsigned long addr = read_cr2(); + struct insn insn; + + if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT)) + return NOTIFY_DONE; + + DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); + this_cpu_core->cpuc_dtrace_illval = addr; + + kernel_insn_init(&insn, (void *)dargs->regs->ip); + insn_get_length(&insn); + + dargs->regs->ip += insn.length; + + return NOTIFY_OK | NOTIFY_STOP_MASK; + } + if (val == DIE_GPF) { + struct insn insn; + + if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT)) + return NOTIFY_DONE; + + DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); + + kernel_insn_init(&insn, (void *)dargs->regs->ip); + insn_get_length(&insn); + + dargs->regs->ip += insn.length; + + return NOTIFY_DONE; + } + if (val != DIE_TRAP || dargs->trapnr != 6) return NOTIFY_DONE; -printk(KERN_INFO "dtrace_die_notifier: TRAP %d, IP %lx\n", dargs->trapnr, dargs->regs->ip); for (hdlr = dtrace_invop_hdlrs; hdlr != NULL; hdlr = hdlr->dtih_next) { if ((rval = hdlr->dtih_func(dargs->regs)) != 0) @@ -261,7 +294,6 @@ printk(KERN_INFO "dtrace_die_notifier: TRAP %d, IP %lx\n", dargs->trapnr, dargs- if (rval != 0) { dargs->regs->ip++; -printk(KERN_INFO "dtrace_die_notifier: TRAP %d, New IP %lx\n", dargs->trapnr, dargs->regs->ip); return NOTIFY_OK | NOTIFY_STOP_MASK; } -- 2.50.1