diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-05 14:59:02 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-08-04 23:29:03 -0400 |
commit | 65b8a9b4d5525348e55cf63a43517610ee8e0970 (patch) | |
tree | 1661d4bc368c768e803a1de4ddb0191bb71acab8 /kernel/module.c | |
parent | f91a13bb99b73961d4e2743a6ff296ac553abc4f (diff) |
module: refactor load_module part 2
Here's a second one. It's slightly less trivial - since we now have error
cases - and equally untested so it may well be totally broken. But it also
cleans up a bit more, and avoids one of the goto targets, because the
"move_module()" helper now does both allocations or none.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'kernel/module.c')
-rw-r--r-- | kernel/module.c | 129 |
1 files changed, 72 insertions, 57 deletions
diff --git a/kernel/module.c b/kernel/module.c index 12905ed44393..19ddcb0dba36 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -2082,7 +2082,8 @@ static void *module_alloc_update_bounds(unsigned long size) | |||
2082 | 2082 | ||
2083 | #ifdef CONFIG_DEBUG_KMEMLEAK | 2083 | #ifdef CONFIG_DEBUG_KMEMLEAK |
2084 | static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, | 2084 | static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, |
2085 | Elf_Shdr *sechdrs, char *secstrings) | 2085 | const Elf_Shdr *sechdrs, |
2086 | const char *secstrings) | ||
2086 | { | 2087 | { |
2087 | unsigned int i; | 2088 | unsigned int i; |
2088 | 2089 | ||
@@ -2102,7 +2103,8 @@ static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, | |||
2102 | } | 2103 | } |
2103 | #else | 2104 | #else |
2104 | static inline void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, | 2105 | static inline void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr, |
2105 | Elf_Shdr *sechdrs, char *secstrings) | 2106 | Elf_Shdr *sechdrs, |
2107 | const char *secstrings) | ||
2106 | { | 2108 | { |
2107 | } | 2109 | } |
2108 | #endif | 2110 | #endif |
@@ -2172,6 +2174,70 @@ static void find_module_sections(struct module *mod, Elf_Ehdr *hdr, | |||
2172 | #endif | 2174 | #endif |
2173 | } | 2175 | } |
2174 | 2176 | ||
2177 | static struct module *move_module(struct module *mod, | ||
2178 | Elf_Ehdr *hdr, Elf_Shdr *sechdrs, | ||
2179 | const char *secstrings, unsigned modindex) | ||
2180 | { | ||
2181 | int i; | ||
2182 | void *ptr; | ||
2183 | |||
2184 | /* Do the allocs. */ | ||
2185 | ptr = module_alloc_update_bounds(mod->core_size); | ||
2186 | /* | ||
2187 | * The pointer to this block is stored in the module structure | ||
2188 | * which is inside the block. Just mark it as not being a | ||
2189 | * leak. | ||
2190 | */ | ||
2191 | kmemleak_not_leak(ptr); | ||
2192 | if (!ptr) | ||
2193 | return ERR_PTR(-ENOMEM); | ||
2194 | |||
2195 | memset(ptr, 0, mod->core_size); | ||
2196 | mod->module_core = ptr; | ||
2197 | |||
2198 | ptr = module_alloc_update_bounds(mod->init_size); | ||
2199 | /* | ||
2200 | * The pointer to this block is stored in the module structure | ||
2201 | * which is inside the block. This block doesn't need to be | ||
2202 | * scanned as it contains data and code that will be freed | ||
2203 | * after the module is initialized. | ||
2204 | */ | ||
2205 | kmemleak_ignore(ptr); | ||
2206 | if (!ptr && mod->init_size) { | ||
2207 | module_free(mod, mod->module_core); | ||
2208 | return ERR_PTR(-ENOMEM); | ||
2209 | } | ||
2210 | memset(ptr, 0, mod->init_size); | ||
2211 | mod->module_init = ptr; | ||
2212 | |||
2213 | /* Transfer each section which specifies SHF_ALLOC */ | ||
2214 | DEBUGP("final section addresses:\n"); | ||
2215 | for (i = 0; i < hdr->e_shnum; i++) { | ||
2216 | void *dest; | ||
2217 | |||
2218 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
2219 | continue; | ||
2220 | |||
2221 | if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK) | ||
2222 | dest = mod->module_init | ||
2223 | + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK); | ||
2224 | else | ||
2225 | dest = mod->module_core + sechdrs[i].sh_entsize; | ||
2226 | |||
2227 | if (sechdrs[i].sh_type != SHT_NOBITS) | ||
2228 | memcpy(dest, (void *)sechdrs[i].sh_addr, | ||
2229 | sechdrs[i].sh_size); | ||
2230 | /* Update sh_addr to point to copy in image. */ | ||
2231 | sechdrs[i].sh_addr = (unsigned long)dest; | ||
2232 | DEBUGP("\t0x%lx %s\n", | ||
2233 | sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name); | ||
2234 | } | ||
2235 | /* Module has been moved. */ | ||
2236 | mod = (void *)sechdrs[modindex].sh_addr; | ||
2237 | kmemleak_load_module(mod, hdr, sechdrs, secstrings); | ||
2238 | return mod; | ||
2239 | } | ||
2240 | |||
2175 | /* Allocate and load the module: note that size of section 0 is always | 2241 | /* Allocate and load the module: note that size of section 0 is always |
2176 | zero, and we rely on this for optional sections. */ | 2242 | zero, and we rely on this for optional sections. */ |
2177 | static noinline struct module *load_module(void __user *umod, | 2243 | static noinline struct module *load_module(void __user *umod, |
@@ -2188,7 +2254,6 @@ static noinline struct module *load_module(void __user *umod, | |||
2188 | unsigned int modindex, versindex, infoindex, pcpuindex; | 2254 | unsigned int modindex, versindex, infoindex, pcpuindex; |
2189 | struct module *mod; | 2255 | struct module *mod; |
2190 | long err = 0; | 2256 | long err = 0; |
2191 | void *ptr = NULL; /* Stops spurious gcc warning */ | ||
2192 | unsigned long symoffs, stroffs, *strmap; | 2257 | unsigned long symoffs, stroffs, *strmap; |
2193 | void __percpu *percpu; | 2258 | void __percpu *percpu; |
2194 | struct _ddebug *debug = NULL; | 2259 | struct _ddebug *debug = NULL; |
@@ -2342,61 +2407,12 @@ static noinline struct module *load_module(void __user *umod, | |||
2342 | symoffs = layout_symtab(mod, sechdrs, symindex, strindex, hdr, | 2407 | symoffs = layout_symtab(mod, sechdrs, symindex, strindex, hdr, |
2343 | secstrings, &stroffs, strmap); | 2408 | secstrings, &stroffs, strmap); |
2344 | 2409 | ||
2345 | /* Do the allocs. */ | 2410 | /* Allocate and move to the final place */ |
2346 | ptr = module_alloc_update_bounds(mod->core_size); | 2411 | mod = move_module(mod, hdr, sechdrs, secstrings, modindex); |
2347 | /* | 2412 | if (IS_ERR(mod)) { |
2348 | * The pointer to this block is stored in the module structure | 2413 | err = PTR_ERR(mod); |
2349 | * which is inside the block. Just mark it as not being a | ||
2350 | * leak. | ||
2351 | */ | ||
2352 | kmemleak_not_leak(ptr); | ||
2353 | if (!ptr) { | ||
2354 | err = -ENOMEM; | ||
2355 | goto free_percpu; | 2414 | goto free_percpu; |
2356 | } | 2415 | } |
2357 | memset(ptr, 0, mod->core_size); | ||
2358 | mod->module_core = ptr; | ||
2359 | |||
2360 | ptr = module_alloc_update_bounds(mod->init_size); | ||
2361 | /* | ||
2362 | * The pointer to this block is stored in the module structure | ||
2363 | * which is inside the block. This block doesn't need to be | ||
2364 | * scanned as it contains data and code that will be freed | ||
2365 | * after the module is initialized. | ||
2366 | */ | ||
2367 | kmemleak_ignore(ptr); | ||
2368 | if (!ptr && mod->init_size) { | ||
2369 | err = -ENOMEM; | ||
2370 | goto free_core; | ||
2371 | } | ||
2372 | memset(ptr, 0, mod->init_size); | ||
2373 | mod->module_init = ptr; | ||
2374 | |||
2375 | /* Transfer each section which specifies SHF_ALLOC */ | ||
2376 | DEBUGP("final section addresses:\n"); | ||
2377 | for (i = 0; i < hdr->e_shnum; i++) { | ||
2378 | void *dest; | ||
2379 | |||
2380 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
2381 | continue; | ||
2382 | |||
2383 | if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK) | ||
2384 | dest = mod->module_init | ||
2385 | + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK); | ||
2386 | else | ||
2387 | dest = mod->module_core + sechdrs[i].sh_entsize; | ||
2388 | |||
2389 | if (sechdrs[i].sh_type != SHT_NOBITS) | ||
2390 | memcpy(dest, (void *)sechdrs[i].sh_addr, | ||
2391 | sechdrs[i].sh_size); | ||
2392 | /* Update sh_addr to point to copy in image. */ | ||
2393 | sechdrs[i].sh_addr = (unsigned long)dest; | ||
2394 | DEBUGP("\t0x%lx %s\n", | ||
2395 | sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name); | ||
2396 | } | ||
2397 | /* Module has been moved. */ | ||
2398 | mod = (void *)sechdrs[modindex].sh_addr; | ||
2399 | kmemleak_load_module(mod, hdr, sechdrs, secstrings); | ||
2400 | 2416 | ||
2401 | #if defined(CONFIG_MODULE_UNLOAD) | 2417 | #if defined(CONFIG_MODULE_UNLOAD) |
2402 | mod->refptr = alloc_percpu(struct module_ref); | 2418 | mod->refptr = alloc_percpu(struct module_ref); |
@@ -2580,7 +2596,6 @@ static noinline struct module *load_module(void __user *umod, | |||
2580 | free_init: | 2596 | free_init: |
2581 | #endif | 2597 | #endif |
2582 | module_free(mod, mod->module_init); | 2598 | module_free(mod, mod->module_init); |
2583 | free_core: | ||
2584 | module_free(mod, mod->module_core); | 2599 | module_free(mod, mod->module_core); |
2585 | /* mod will be freed with core. Don't access it beyond this line! */ | 2600 | /* mod will be freed with core. Don't access it beyond this line! */ |
2586 | free_percpu: | 2601 | free_percpu: |