diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-11 17:15:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-11 17:15:57 -0400 |
commit | 512626a04e72aca60effe111fa0333ed0b195d21 (patch) | |
tree | c22e23b0dcc2dd2ff5a9a96a007de6799e9223de /kernel/module.c | |
parent | 8a1ca8cedd108c8e76a6ab34079d0bbb4f244799 (diff) | |
parent | 3aa27bbe7a6536d1ec859d3a97caf3319b5081b7 (diff) |
Merge branch 'for-linus' of git://linux-arm.org/linux-2.6
* 'for-linus' of git://linux-arm.org/linux-2.6:
kmemleak: Add the corresponding MAINTAINERS entry
kmemleak: Simple testing module for kmemleak
kmemleak: Enable the building of the memory leak detector
kmemleak: Remove some of the kmemleak false positives
kmemleak: Add modules support
kmemleak: Add kmemleak_alloc callback from alloc_large_system_hash
kmemleak: Add the vmalloc memory allocation/freeing hooks
kmemleak: Add the slub memory allocation/freeing hooks
kmemleak: Add the slob memory allocation/freeing hooks
kmemleak: Add the slab memory allocation/freeing hooks
kmemleak: Add documentation on the memory leak detector
kmemleak: Add the base support
Manual conflict resolution (with the slab/earlyboot changes) in:
drivers/char/vt.c
init/main.c
mm/slab.c
Diffstat (limited to 'kernel/module.c')
-rw-r--r-- | kernel/module.c | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/kernel/module.c b/kernel/module.c index 278e9b6762bb..35f7de00bf0d 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include <linux/ftrace.h> | 53 | #include <linux/ftrace.h> |
54 | #include <linux/async.h> | 54 | #include <linux/async.h> |
55 | #include <linux/percpu.h> | 55 | #include <linux/percpu.h> |
56 | #include <linux/kmemleak.h> | ||
56 | 57 | ||
57 | #if 0 | 58 | #if 0 |
58 | #define DEBUGP printk | 59 | #define DEBUGP printk |
@@ -433,6 +434,7 @@ static void *percpu_modalloc(unsigned long size, unsigned long align, | |||
433 | unsigned long extra; | 434 | unsigned long extra; |
434 | unsigned int i; | 435 | unsigned int i; |
435 | void *ptr; | 436 | void *ptr; |
437 | int cpu; | ||
436 | 438 | ||
437 | if (align > PAGE_SIZE) { | 439 | if (align > PAGE_SIZE) { |
438 | printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n", | 440 | printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n", |
@@ -462,6 +464,11 @@ static void *percpu_modalloc(unsigned long size, unsigned long align, | |||
462 | if (!split_block(i, size)) | 464 | if (!split_block(i, size)) |
463 | return NULL; | 465 | return NULL; |
464 | 466 | ||
467 | /* add the per-cpu scanning areas */ | ||
468 | for_each_possible_cpu(cpu) | ||
469 | kmemleak_alloc(ptr + per_cpu_offset(cpu), size, 0, | ||
470 | GFP_KERNEL); | ||
471 | |||
465 | /* Mark allocated */ | 472 | /* Mark allocated */ |
466 | pcpu_size[i] = -pcpu_size[i]; | 473 | pcpu_size[i] = -pcpu_size[i]; |
467 | return ptr; | 474 | return ptr; |
@@ -476,6 +483,7 @@ static void percpu_modfree(void *freeme) | |||
476 | { | 483 | { |
477 | unsigned int i; | 484 | unsigned int i; |
478 | void *ptr = __per_cpu_start + block_size(pcpu_size[0]); | 485 | void *ptr = __per_cpu_start + block_size(pcpu_size[0]); |
486 | int cpu; | ||
479 | 487 | ||
480 | /* First entry is core kernel percpu data. */ | 488 | /* First entry is core kernel percpu data. */ |
481 | for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) { | 489 | for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) { |
@@ -487,6 +495,10 @@ static void percpu_modfree(void *freeme) | |||
487 | BUG(); | 495 | BUG(); |
488 | 496 | ||
489 | free: | 497 | free: |
498 | /* remove the per-cpu scanning areas */ | ||
499 | for_each_possible_cpu(cpu) | ||
500 | kmemleak_free(freeme + per_cpu_offset(cpu)); | ||
501 | |||
490 | /* Merge with previous? */ | 502 | /* Merge with previous? */ |
491 | if (pcpu_size[i-1] >= 0) { | 503 | if (pcpu_size[i-1] >= 0) { |
492 | pcpu_size[i-1] += pcpu_size[i]; | 504 | pcpu_size[i-1] += pcpu_size[i]; |
@@ -1879,6 +1891,36 @@ static void *module_alloc_update_bounds(unsigned long size) | |||
1879 | return ret; | 1891 | return ret; |
1880 | } | 1892 | } |
1881 | 1893 | ||
1894 | #ifdef CONFIG_DEBUG_KMEMLEAK | ||
1895 | static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, | ||
1896 | Elf_Shdr *sechdrs, char *secstrings) | ||
1897 | { | ||
1898 | unsigned int i; | ||
1899 | |||
1900 | /* only scan the sections containing data */ | ||
1901 | kmemleak_scan_area(mod->module_core, (unsigned long)mod - | ||
1902 | (unsigned long)mod->module_core, | ||
1903 | sizeof(struct module), GFP_KERNEL); | ||
1904 | |||
1905 | for (i = 1; i < hdr->e_shnum; i++) { | ||
1906 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
1907 | continue; | ||
1908 | if (strncmp(secstrings + sechdrs[i].sh_name, ".data", 5) != 0 | ||
1909 | && strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) != 0) | ||
1910 | continue; | ||
1911 | |||
1912 | kmemleak_scan_area(mod->module_core, sechdrs[i].sh_addr - | ||
1913 | (unsigned long)mod->module_core, | ||
1914 | sechdrs[i].sh_size, GFP_KERNEL); | ||
1915 | } | ||
1916 | } | ||
1917 | #else | ||
1918 | static inline void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, | ||
1919 | Elf_Shdr *sechdrs, char *secstrings) | ||
1920 | { | ||
1921 | } | ||
1922 | #endif | ||
1923 | |||
1882 | /* Allocate and load the module: note that size of section 0 is always | 1924 | /* Allocate and load the module: note that size of section 0 is always |
1883 | zero, and we rely on this for optional sections. */ | 1925 | zero, and we rely on this for optional sections. */ |
1884 | static noinline struct module *load_module(void __user *umod, | 1926 | static noinline struct module *load_module(void __user *umod, |
@@ -2049,6 +2091,12 @@ static noinline struct module *load_module(void __user *umod, | |||
2049 | 2091 | ||
2050 | /* Do the allocs. */ | 2092 | /* Do the allocs. */ |
2051 | ptr = module_alloc_update_bounds(mod->core_size); | 2093 | ptr = module_alloc_update_bounds(mod->core_size); |
2094 | /* | ||
2095 | * The pointer to this block is stored in the module structure | ||
2096 | * which is inside the block. Just mark it as not being a | ||
2097 | * leak. | ||
2098 | */ | ||
2099 | kmemleak_not_leak(ptr); | ||
2052 | if (!ptr) { | 2100 | if (!ptr) { |
2053 | err = -ENOMEM; | 2101 | err = -ENOMEM; |
2054 | goto free_percpu; | 2102 | goto free_percpu; |
@@ -2057,6 +2105,13 @@ static noinline struct module *load_module(void __user *umod, | |||
2057 | mod->module_core = ptr; | 2105 | mod->module_core = ptr; |
2058 | 2106 | ||
2059 | ptr = module_alloc_update_bounds(mod->init_size); | 2107 | ptr = module_alloc_update_bounds(mod->init_size); |
2108 | /* | ||
2109 | * The pointer to this block is stored in the module structure | ||
2110 | * which is inside the block. This block doesn't need to be | ||
2111 | * scanned as it contains data and code that will be freed | ||
2112 | * after the module is initialized. | ||
2113 | */ | ||
2114 | kmemleak_ignore(ptr); | ||
2060 | if (!ptr && mod->init_size) { | 2115 | if (!ptr && mod->init_size) { |
2061 | err = -ENOMEM; | 2116 | err = -ENOMEM; |
2062 | goto free_core; | 2117 | goto free_core; |
@@ -2087,6 +2142,7 @@ static noinline struct module *load_module(void __user *umod, | |||
2087 | } | 2142 | } |
2088 | /* Module has been moved. */ | 2143 | /* Module has been moved. */ |
2089 | mod = (void *)sechdrs[modindex].sh_addr; | 2144 | mod = (void *)sechdrs[modindex].sh_addr; |
2145 | kmemleak_load_module(mod, hdr, sechdrs, secstrings); | ||
2090 | 2146 | ||
2091 | #if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP) | 2147 | #if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP) |
2092 | mod->refptr = percpu_modalloc(sizeof(local_t), __alignof__(local_t), | 2148 | mod->refptr = percpu_modalloc(sizeof(local_t), __alignof__(local_t), |