diff options
author | Akinobu Mita <akinobu.mita@gmail.com> | 2009-03-31 18:23:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-01 11:59:13 -0400 |
commit | 6a11f75b6a17b5d9ac5025f8d048382fd1f47377 (patch) | |
tree | a3415cd897823975b927f89a233d2703bf97ba2b | |
parent | 610a77e04a8d9fe8764dc484e2182fa251ce1cc2 (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.c | 18 | ||||
-rw-r--r-- | arch/powerpc/Kconfig | 3 | ||||
-rw-r--r-- | arch/powerpc/Kconfig.debug | 1 | ||||
-rw-r--r-- | arch/s390/Kconfig | 3 | ||||
-rw-r--r-- | arch/s390/Kconfig.debug | 1 | ||||
-rw-r--r-- | arch/sparc/Kconfig | 3 | ||||
-rw-r--r-- | arch/sparc/Kconfig.debug | 3 | ||||
-rw-r--r-- | arch/x86/Kconfig | 3 | ||||
-rw-r--r-- | arch/x86/Kconfig.debug | 1 | ||||
-rw-r--r-- | include/linux/mm_types.h | 4 | ||||
-rw-r--r-- | include/linux/page-debug-flags.h | 30 | ||||
-rw-r--r-- | include/linux/poison.h | 3 | ||||
-rw-r--r-- | lib/Kconfig.debug | 1 | ||||
-rw-r--r-- | mm/Kconfig.debug | 17 | ||||
-rw-r--r-- | mm/Makefile | 1 | ||||
-rw-r--r-- | mm/debug-pagealloc.c | 129 |
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 | ||
265 | void kernel_map_pages(struct page *page, int numpages, int enable) | ||
266 | { | ||
267 | |||
268 | } | ||
269 | EXPORT_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 | ||
231 | config ARCH_SUPPORTS_DEBUG_PAGEALLOC | ||
232 | def_bool y | ||
233 | |||
231 | source "init/Kconfig" | 234 | source "init/Kconfig" |
232 | 235 | ||
233 | source "kernel/Kconfig.freezer" | 236 | source "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 | |||
30 | config DEBUG_PAGEALLOC | 30 | config 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 | |||
72 | config VIRT_CPU_ACCOUNTING | 72 | config VIRT_CPU_ACCOUNTING |
73 | def_bool y | 73 | def_bool y |
74 | 74 | ||
75 | config ARCH_SUPPORTS_DEBUG_PAGEALLOC | ||
76 | def_bool y | ||
77 | |||
75 | mainmenu "Linux Kernel Configuration" | 78 | mainmenu "Linux Kernel Configuration" |
76 | 79 | ||
77 | config S390 | 80 | config 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" | |||
9 | config DEBUG_PAGEALLOC | 9 | config 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 | |||
124 | config OF | 124 | config OF |
125 | def_bool y | 125 | def_bool y |
126 | 126 | ||
127 | config ARCH_SUPPORTS_DEBUG_PAGEALLOC | ||
128 | def_bool y if SPARC64 | ||
129 | |||
127 | source "init/Kconfig" | 130 | source "init/Kconfig" |
128 | 131 | ||
129 | source "kernel/Kconfig.freezer" | 132 | source "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 | ||
25 | config DEBUG_PAGEALLOC | 25 | config 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 | |||
165 | config ARCH_SUPPORTS_OPTIMIZED_INLINING | 165 | config ARCH_SUPPORTS_OPTIMIZED_INLINING |
166 | def_bool y | 166 | def_bool y |
167 | 167 | ||
168 | config 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/: |
169 | config GENERIC_HARDIRQS | 172 | config 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 | |||
75 | config DEBUG_PAGEALLOC | 75 | config 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 | ||
179 | struct core_thread { | 183 | struct 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 | |||
14 | enum 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 | ||
799 | source mm/Kconfig.debug | ||
799 | source kernel/trace/Kconfig | 800 | source kernel/trace/Kconfig |
800 | 801 | ||
801 | config PROVIDE_OHCI1394_DMA_INIT | 802 | config 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 @@ | |||
1 | config WANT_PAGE_DEBUG_FLAGS | ||
2 | bool | ||
3 | |||
4 | config 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 | |||
24 | obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o | 24 | obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o |
25 | obj-$(CONFIG_SLOB) += slob.o | 25 | obj-$(CONFIG_SLOB) += slob.o |
26 | obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o | 26 | obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o |
27 | obj-$(CONFIG_PAGE_POISONING) += debug-pagealloc.o | ||
27 | obj-$(CONFIG_SLAB) += slab.o | 28 | obj-$(CONFIG_SLAB) += slab.o |
28 | obj-$(CONFIG_SLUB) += slub.o | 29 | obj-$(CONFIG_SLUB) += slub.o |
29 | obj-$(CONFIG_FAILSLAB) += failslab.o | 30 | obj-$(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 | |||
6 | static inline void set_page_poison(struct page *page) | ||
7 | { | ||
8 | __set_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags); | ||
9 | } | ||
10 | |||
11 | static inline void clear_page_poison(struct page *page) | ||
12 | { | ||
13 | __clear_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags); | ||
14 | } | ||
15 | |||
16 | static inline bool page_poison(struct page *page) | ||
17 | { | ||
18 | return test_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags); | ||
19 | } | ||
20 | |||
21 | static 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 | |||
32 | static 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 | |||
45 | static 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 | |||
53 | static 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 | |||
60 | static 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 | |||
89 | static 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 | |||
98 | static 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 | |||
112 | static 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 | |||
120 | void 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 | } | ||