Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86: add hooks for kmemcheck

The hooks that we modify are:
- Page fault handler (to handle kmemcheck faults)
- Debug exception handler (to hide pages after single-stepping
the instruction that caused the page fault)

Also redefine memset() to use the optimized version if kmemcheck is
enabled.

(Thanks to Pekka Enberg for minimizing the impact on the page fault
handler.)

As kmemcheck doesn't handle MMX/SSE instructions (yet), we also disable
the optimized xor code, and rely instead on the generic C implementation
in order to avoid false-positive warnings.

Signed-off-by: Vegard Nossum <vegardno@ifi.uio.no>

[whitespace fixlet]
Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

[rebased for mainline inclusion]
Signed-off-by: Vegard Nossum <vegardno@ifi.uio.no>

+66 -5
+8
arch/x86/include/asm/string_32.h
··· 177 177 * No 3D Now! 178 178 */ 179 179 180 + #ifndef CONFIG_KMEMCHECK 180 181 #define memcpy(t, f, n) \ 181 182 (__builtin_constant_p((n)) \ 182 183 ? __constant_memcpy((t), (f), (n)) \ 183 184 : __memcpy((t), (f), (n))) 185 + #else 186 + /* 187 + * kmemcheck becomes very happy if we use the REP instructions unconditionally, 188 + * because it means that we know both memory operands in advance. 189 + */ 190 + #define memcpy(t, f, n) __memcpy((t), (f), (n)) 191 + #endif 184 192 185 193 #endif 186 194
+8
arch/x86/include/asm/string_64.h
··· 27 27 function. */ 28 28 29 29 #define __HAVE_ARCH_MEMCPY 1 30 + #ifndef CONFIG_KMEMCHECK 30 31 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 31 32 extern void *memcpy(void *to, const void *from, size_t len); 32 33 #else ··· 42 41 __ret = __builtin_memcpy((dst), (src), __len); \ 43 42 __ret; \ 44 43 }) 44 + #endif 45 + #else 46 + /* 47 + * kmemcheck becomes very happy if we use the REP instructions unconditionally, 48 + * because it means that we know both memory operands in advance. 49 + */ 50 + #define memcpy(dst, src, len) __inline_memcpy((dst), (src), (len)) 45 51 #endif 46 52 47 53 #define __HAVE_ARCH_MEMSET
+5
arch/x86/include/asm/xor.h
··· 1 + #ifdef CONFIG_KMEMCHECK 2 + /* kmemcheck doesn't handle MMX/SSE/SSE2 instructions */ 3 + # include <asm-generic/xor.h> 4 + #else 1 5 #ifdef CONFIG_X86_32 2 6 # include "xor_32.h" 3 7 #else 4 8 # include "xor_64.h" 9 + #endif 5 10 #endif
+23
arch/x86/kernel/cpu/intel.c
··· 86 86 */ 87 87 if (c->x86 == 6 && c->x86_model < 15) 88 88 clear_cpu_cap(c, X86_FEATURE_PAT); 89 + 90 + #ifdef CONFIG_KMEMCHECK 91 + /* 92 + * P4s have a "fast strings" feature which causes single- 93 + * stepping REP instructions to only generate a #DB on 94 + * cache-line boundaries. 95 + * 96 + * Ingo Molnar reported a Pentium D (model 6) and a Xeon 97 + * (model 2) with the same problem. 98 + */ 99 + if (c->x86 == 15) { 100 + u64 misc_enable; 101 + 102 + rdmsrl(MSR_IA32_MISC_ENABLE, misc_enable); 103 + 104 + if (misc_enable & MSR_IA32_MISC_ENABLE_FAST_STRING) { 105 + printk(KERN_INFO "kmemcheck: Disabling fast string operations\n"); 106 + 107 + misc_enable &= ~MSR_IA32_MISC_ENABLE_FAST_STRING; 108 + wrmsrl(MSR_IA32_MISC_ENABLE, misc_enable); 109 + } 110 + } 111 + #endif 89 112 } 90 113 91 114 #ifdef CONFIG_X86_32
+5
arch/x86/kernel/traps.c
··· 45 45 #include <linux/edac.h> 46 46 #endif 47 47 48 + #include <asm/kmemcheck.h> 48 49 #include <asm/stacktrace.h> 49 50 #include <asm/processor.h> 50 51 #include <asm/debugreg.h> ··· 534 533 int si_code; 535 534 536 535 get_debugreg(condition, 6); 536 + 537 + /* Catch kmemcheck conditions first of all! */ 538 + if (condition & DR_STEP && kmemcheck_trap(regs)) 539 + return; 537 540 538 541 /* 539 542 * The processor cleared BTF, so don't mark that we need it set.
+15 -3
arch/x86/mm/fault.c
··· 14 14 15 15 #include <asm/traps.h> /* dotraplinkage, ... */ 16 16 #include <asm/pgalloc.h> /* pgd_*(), ... */ 17 + #include <asm/kmemcheck.h> /* kmemcheck_*(), ... */ 17 18 18 19 /* 19 20 * Page fault error code bits: ··· 957 956 /* Get the faulting address: */ 958 957 address = read_cr2(); 959 958 959 + /* 960 + * Detect and handle instructions that would cause a page fault for 961 + * both a tracked kernel page and a userspace page. 962 + */ 963 + if (kmemcheck_active(regs)) 964 + kmemcheck_hide(regs); 965 + 960 966 if (unlikely(kmmio_fault(regs, address))) 961 967 return; 962 968 ··· 981 973 * protection error (error_code & 9) == 0. 982 974 */ 983 975 if (unlikely(fault_in_kernel_space(address))) { 984 - if (!(error_code & (PF_RSVD|PF_USER|PF_PROT)) && 985 - vmalloc_fault(address) >= 0) 986 - return; 976 + if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) { 977 + if (vmalloc_fault(address) >= 0) 978 + return; 979 + 980 + if (kmemcheck_fault(regs, address, error_code)) 981 + return; 982 + } 987 983 988 984 /* Can handle a stale RO->RW TLB: */ 989 985 if (spurious_fault(error_code, address))
+1 -1
arch/x86/mm/init.c
··· 213 213 if (!after_bootmem) 214 214 init_gbpages(); 215 215 216 - #ifdef CONFIG_DEBUG_PAGEALLOC 216 + #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK) 217 217 /* 218 218 * For CONFIG_DEBUG_PAGEALLOC, identity mapping will use small pages. 219 219 * This will simplify cpa(), which otherwise needs to support splitting
+1 -1
arch/x86/mm/init_32.c
··· 111 111 pte_t *page_table = NULL; 112 112 113 113 if (after_bootmem) { 114 - #ifdef CONFIG_DEBUG_PAGEALLOC 114 + #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK) 115 115 page_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); 116 116 #endif 117 117 if (!page_table)