aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkinobu Mita <akinobu.mita@gmail.com>2009-03-31 18:23:17 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-01 11:59:13 -0400
commit6a11f75b6a17b5d9ac5025f8d048382fd1f47377 (patch)
treea3415cd897823975b927f89a233d2703bf97ba2b
parent610a77e04a8d9fe8764dc484e2182fa251ce1cc2 (diff)
generic debug pagealloc
CONFIG_DEBUG_PAGEALLOC is now supported by x86, powerpc, sparc64, and s390. This patch implements it for the rest of the architectures by filling the pages with poison byte patterns after free_pages() and verifying the poison patterns before alloc_pages(). This generic one cannot detect invalid page accesses immediately but invalid read access may cause invalid dereference by poisoned memory and invalid write access can be detected after a long delay. Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Cc: <linux-arch@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/avr32/mm/fault.c18
-rw-r--r--arch/powerpc/Kconfig3
-rw-r--r--arch/powerpc/Kconfig.debug1
-rw-r--r--arch/s390/Kconfig3
-rw-r--r--arch/s390/Kconfig.debug1
-rw-r--r--arch/sparc/Kconfig3
-rw-r--r--arch/sparc/Kconfig.debug3
-rw-r--r--arch/x86/Kconfig3
-rw-r--r--arch/x86/Kconfig.debug1
-rw-r--r--include/linux/mm_types.h4
-rw-r--r--include/linux/page-debug-flags.h30
-rw-r--r--include/linux/poison.h3
-rw-r--r--lib/Kconfig.debug1
-rw-r--r--mm/Kconfig.debug17
-rw-r--r--mm/Makefile1
-rw-r--r--mm/debug-pagealloc.c129
16 files changed, 202 insertions, 19 deletions
diff --git a/arch/avr32/mm/fault.c b/arch/avr32/mm/fault.c
index ce4e4296b954..62d4abbaa654 100644
--- a/arch/avr32/mm/fault.c
+++ b/arch/avr32/mm/fault.c
@@ -250,21 +250,3 @@ asmlinkage void do_bus_error(unsigned long addr, int write_access,
250 dump_dtlb(); 250 dump_dtlb();
251 die("Bus Error", regs, SIGKILL); 251 die("Bus Error", regs, SIGKILL);
252} 252}
253
254/*
255 * This functionality is currently not possible to implement because
256 * we're using segmentation to ensure a fixed mapping of the kernel
257 * virtual address space.
258 *
259 * It would be possible to implement this, but it would require us to
260 * disable segmentation at startup and load the kernel mappings into
261 * the TLB like any other pages. There will be lots of trickery to
262 * avoid recursive invocation of the TLB miss handler, though...
263 */
264#ifdef CONFIG_DEBUG_PAGEALLOC
265void kernel_map_pages(struct page *page, int numpages, int enable)
266{
267
268}
269EXPORT_SYMBOL(kernel_map_pages);
270#endif
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index ad6b1c084fe3..45192dce65c4 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -228,6 +228,9 @@ config PPC_OF_PLATFORM_PCI
228 depends on PPC64 # not supported on 32 bits yet 228 depends on PPC64 # not supported on 32 bits yet
229 default n 229 default n
230 230
231config ARCH_SUPPORTS_DEBUG_PAGEALLOC
232 def_bool y
233
231source "init/Kconfig" 234source "init/Kconfig"
232 235
233source "kernel/Kconfig.freezer" 236source "kernel/Kconfig.freezer"
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 22091bbfdc9b..6aa0b5e087cd 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -30,6 +30,7 @@ config DEBUG_STACK_USAGE
30config DEBUG_PAGEALLOC 30config DEBUG_PAGEALLOC
31 bool "Debug page memory allocations" 31 bool "Debug page memory allocations"
32 depends on DEBUG_KERNEL && !HIBERNATION 32 depends on DEBUG_KERNEL && !HIBERNATION
33 depends on ARCH_SUPPORTS_DEBUG_PAGEALLOC
33 help 34 help
34 Unmap pages from the kernel linear mapping after free_pages(). 35 Unmap pages from the kernel linear mapping after free_pages().
35 This results in a large slowdown, but helps to find certain types 36 This results in a large slowdown, but helps to find certain types
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 2a8af5e16345..dcb667c4375a 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -72,6 +72,9 @@ config PGSTE
72config VIRT_CPU_ACCOUNTING 72config VIRT_CPU_ACCOUNTING
73 def_bool y 73 def_bool y
74 74
75config ARCH_SUPPORTS_DEBUG_PAGEALLOC
76 def_bool y
77
75mainmenu "Linux Kernel Configuration" 78mainmenu "Linux Kernel Configuration"
76 79
77config S390 80config S390
diff --git a/arch/s390/Kconfig.debug b/arch/s390/Kconfig.debug
index 4599fa06bd82..7e297a3cde34 100644
--- a/arch/s390/Kconfig.debug
+++ b/arch/s390/Kconfig.debug
@@ -9,6 +9,7 @@ source "lib/Kconfig.debug"
9config DEBUG_PAGEALLOC 9config DEBUG_PAGEALLOC
10 bool "Debug page memory allocations" 10 bool "Debug page memory allocations"
11 depends on DEBUG_KERNEL 11 depends on DEBUG_KERNEL
12 depends on ARCH_SUPPORTS_DEBUG_PAGEALLOC
12 help 13 help
13 Unmap pages from the kernel linear mapping after free_pages(). 14 Unmap pages from the kernel linear mapping after free_pages().
14 This results in a slowdown, but helps to find certain types of 15 This results in a slowdown, but helps to find certain types of
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index c3ea215334f6..cc12cd48bbc5 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -124,6 +124,9 @@ config ARCH_NO_VIRT_TO_BUS
124config OF 124config OF
125 def_bool y 125 def_bool y
126 126
127config ARCH_SUPPORTS_DEBUG_PAGEALLOC
128 def_bool y if SPARC64
129
127source "init/Kconfig" 130source "init/Kconfig"
128 131
129source "kernel/Kconfig.freezer" 132source "kernel/Kconfig.freezer"
diff --git a/arch/sparc/Kconfig.debug b/arch/sparc/Kconfig.debug
index b8a15e271bfa..d001b42041a5 100644
--- a/arch/sparc/Kconfig.debug
+++ b/arch/sparc/Kconfig.debug
@@ -24,7 +24,8 @@ config STACK_DEBUG
24 24
25config DEBUG_PAGEALLOC 25config DEBUG_PAGEALLOC
26 bool "Debug page memory allocations" 26 bool "Debug page memory allocations"
27 depends on SPARC64 && DEBUG_KERNEL && !HIBERNATION 27 depends on DEBUG_KERNEL && !HIBERNATION
28 depends on ARCH_SUPPORTS_DEBUG_PAGEALLOC
28 help 29 help
29 Unmap pages from the kernel linear mapping after free_pages(). 30 Unmap pages from the kernel linear mapping after free_pages().
30 This results in a large slowdown, but helps to find certain types 31 This results in a large slowdown, but helps to find certain types
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 45161b816313..748e50a1a152 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -165,6 +165,9 @@ config AUDIT_ARCH
165config ARCH_SUPPORTS_OPTIMIZED_INLINING 165config ARCH_SUPPORTS_OPTIMIZED_INLINING
166 def_bool y 166 def_bool y
167 167
168config ARCH_SUPPORTS_DEBUG_PAGEALLOC
169 def_bool y
170
168# Use the generic interrupt handling code in kernel/irq/: 171# Use the generic interrupt handling code in kernel/irq/:
169config GENERIC_HARDIRQS 172config GENERIC_HARDIRQS
170 bool 173 bool
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index fdb45df608b6..a345cb5447a8 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -75,6 +75,7 @@ config DEBUG_STACK_USAGE
75config DEBUG_PAGEALLOC 75config DEBUG_PAGEALLOC
76 bool "Debug page memory allocations" 76 bool "Debug page memory allocations"
77 depends on DEBUG_KERNEL 77 depends on DEBUG_KERNEL
78 depends on ARCH_SUPPORTS_DEBUG_PAGEALLOC
78 ---help--- 79 ---help---
79 Unmap pages from the kernel linear mapping after free_pages(). 80 Unmap pages from the kernel linear mapping after free_pages().
80 This results in a large slowdown, but helps to find certain types 81 This results in a large slowdown, but helps to find certain types
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index d84feb7bdbf0..ddadb4defe00 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -11,6 +11,7 @@
11#include <linux/rwsem.h> 11#include <linux/rwsem.h>
12#include <linux/completion.h> 12#include <linux/completion.h>
13#include <linux/cpumask.h> 13#include <linux/cpumask.h>
14#include <linux/page-debug-flags.h>
14#include <asm/page.h> 15#include <asm/page.h>
15#include <asm/mmu.h> 16#include <asm/mmu.h>
16 17
@@ -174,6 +175,9 @@ struct vm_area_struct {
174#ifdef CONFIG_NUMA 175#ifdef CONFIG_NUMA
175 struct mempolicy *vm_policy; /* NUMA policy for the VMA */ 176 struct mempolicy *vm_policy; /* NUMA policy for the VMA */
176#endif 177#endif
178#ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
179 unsigned long debug_flags; /* Use atomic bitops on this */
180#endif
177}; 181};
178 182
179struct core_thread { 183struct core_thread {
diff --git a/include/linux/page-debug-flags.h b/include/linux/page-debug-flags.h
new file mode 100644
index 000000000000..b0638fd91e92
--- /dev/null
+++ b/include/linux/page-debug-flags.h
@@ -0,0 +1,30 @@
1#ifndef LINUX_PAGE_DEBUG_FLAGS_H
2#define LINUX_PAGE_DEBUG_FLAGS_H
3
4/*
5 * page->debug_flags bits:
6 *
7 * PAGE_DEBUG_FLAG_POISON is set for poisoned pages. This is used to
8 * implement generic debug pagealloc feature. The pages are filled with
9 * poison patterns and set this flag after free_pages(). The poisoned
10 * pages are verified whether the patterns are not corrupted and clear
11 * the flag before alloc_pages().
12 */
13
14enum page_debug_flags {
15 PAGE_DEBUG_FLAG_POISON, /* Page is poisoned */
16};
17
18/*
19 * Ensure that CONFIG_WANT_PAGE_DEBUG_FLAGS reliably
20 * gets turned off when no debug features are enabling it!
21 */
22
23#ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
24#if !defined(CONFIG_PAGE_POISONING) \
25/* && !defined(CONFIG_PAGE_DEBUG_SOMETHING_ELSE) && ... */
26#error WANT_PAGE_DEBUG_FLAGS is turned on with no debug features!
27#endif
28#endif /* CONFIG_WANT_PAGE_DEBUG_FLAGS */
29
30#endif /* LINUX_PAGE_DEBUG_FLAGS_H */
diff --git a/include/linux/poison.h b/include/linux/poison.h
index 9f31683728fd..6729f7dcd60e 100644
--- a/include/linux/poison.h
+++ b/include/linux/poison.h
@@ -17,6 +17,9 @@
17 */ 17 */
18#define TIMER_ENTRY_STATIC ((void *) 0x74737461) 18#define TIMER_ENTRY_STATIC ((void *) 0x74737461)
19 19
20/********** mm/debug-pagealloc.c **********/
21#define PAGE_POISON 0xaa
22
20/********** mm/slab.c **********/ 23/********** mm/slab.c **********/
21/* 24/*
22 * Magic nums for obj red zoning. 25 * Magic nums for obj red zoning.
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 58bfe7e8faba..9638d99644af 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -796,6 +796,7 @@ config SYSCTL_SYSCALL_CHECK
796 to properly maintain and use. This enables checks that help 796 to properly maintain and use. This enables checks that help
797 you to keep things correct. 797 you to keep things correct.
798 798
799source mm/Kconfig.debug
799source kernel/trace/Kconfig 800source kernel/trace/Kconfig
800 801
801config PROVIDE_OHCI1394_DMA_INIT 802config PROVIDE_OHCI1394_DMA_INIT
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
new file mode 100644
index 000000000000..c8d62d49a44e
--- /dev/null
+++ b/mm/Kconfig.debug
@@ -0,0 +1,17 @@
1config WANT_PAGE_DEBUG_FLAGS
2 bool
3
4config PAGE_POISONING
5 bool "Debug page memory allocations"
6 depends on DEBUG_KERNEL && !ARCH_SUPPORTS_DEBUG_PAGEALLOC
7 depends on !HIBERNATION
8 select DEBUG_PAGEALLOC
9 select WANT_PAGE_DEBUG_FLAGS
10 help
11 Fill the pages with poison patterns after free_pages() and verify
12 the patterns before alloc_pages(). This results in a large slowdown,
13 but helps to find certain types of memory corruptions.
14
15 This option cannot enalbe with hibernation. Otherwise, it will get
16 wrong messages for memory corruption because the free pages are not
17 saved to the suspend image.
diff --git a/mm/Makefile b/mm/Makefile
index 818569b68f46..ec73c68b6015 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o
24obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o 24obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o
25obj-$(CONFIG_SLOB) += slob.o 25obj-$(CONFIG_SLOB) += slob.o
26obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o 26obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o
27obj-$(CONFIG_PAGE_POISONING) += debug-pagealloc.o
27obj-$(CONFIG_SLAB) += slab.o 28obj-$(CONFIG_SLAB) += slab.o
28obj-$(CONFIG_SLUB) += slub.o 29obj-$(CONFIG_SLUB) += slub.o
29obj-$(CONFIG_FAILSLAB) += failslab.o 30obj-$(CONFIG_FAILSLAB) += failslab.o
diff --git a/mm/debug-pagealloc.c b/mm/debug-pagealloc.c
new file mode 100644
index 000000000000..a1e3324de2b5
--- /dev/null
+++ b/mm/debug-pagealloc.c
@@ -0,0 +1,129 @@
1#include <linux/kernel.h>
2#include <linux/mm.h>
3#include <linux/page-debug-flags.h>
4#include <linux/poison.h>
5
6static inline void set_page_poison(struct page *page)
7{
8 __set_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
9}
10
11static inline void clear_page_poison(struct page *page)
12{
13 __clear_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
14}
15
16static inline bool page_poison(struct page *page)
17{
18 return test_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
19}
20
21static void poison_highpage(struct page *page)
22{
23 /*
24 * Page poisoning for highmem pages is not implemented.
25 *
26 * This can be called from interrupt contexts.
27 * So we need to create a new kmap_atomic slot for this
28 * application and it will need interrupt protection.
29 */
30}
31
32static void poison_page(struct page *page)
33{
34 void *addr;
35
36 if (PageHighMem(page)) {
37 poison_highpage(page);
38 return;
39 }
40 set_page_poison(page);
41 addr = page_address(page);
42 memset(addr, PAGE_POISON, PAGE_SIZE);
43}
44
45static void poison_pages(struct page *page, int n)
46{
47 int i;
48
49 for (i = 0; i < n; i++)
50 poison_page(page + i);
51}
52
53static bool single_bit_flip(unsigned char a, unsigned char b)
54{
55 unsigned char error = a ^ b;
56
57 return error && !(error & (error - 1));
58}
59
60static void check_poison_mem(unsigned char *mem, size_t bytes)
61{
62 unsigned char *start;
63 unsigned char *end;
64
65 for (start = mem; start < mem + bytes; start++) {
66 if (*start != PAGE_POISON)
67 break;
68 }
69 if (start == mem + bytes)
70 return;
71
72 for (end = mem + bytes - 1; end > start; end--) {
73 if (*end != PAGE_POISON)
74 break;
75 }
76
77 if (!printk_ratelimit())
78 return;
79 else if (start == end && single_bit_flip(*start, PAGE_POISON))
80 printk(KERN_ERR "pagealloc: single bit error\n");
81 else
82 printk(KERN_ERR "pagealloc: memory corruption\n");
83
84 print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, start,
85 end - start + 1, 1);
86 dump_stack();
87}
88
89static void unpoison_highpage(struct page *page)
90{
91 /*
92 * See comment in poison_highpage().
93 * Highmem pages should not be poisoned for now
94 */
95 BUG_ON(page_poison(page));
96}
97
98static void unpoison_page(struct page *page)
99{
100 if (PageHighMem(page)) {
101 unpoison_highpage(page);
102 return;
103 }
104 if (page_poison(page)) {
105 void *addr = page_address(page);
106
107 check_poison_mem(addr, PAGE_SIZE);
108 clear_page_poison(page);
109 }
110}
111
112static void unpoison_pages(struct page *page, int n)
113{
114 int i;
115
116 for (i = 0; i < n; i++)
117 unpoison_page(page + i);
118}
119
120void kernel_map_pages(struct page *page, int numpages, int enable)
121{
122 if (!debug_pagealloc_enabled)
123 return;
124
125 if (enable)
126 unpoison_pages(page, numpages);
127 else
128 poison_pages(page, numpages);
129}