]> Pileus Git - ~andy/linux/blobdiff - kernel/module.c
module: refactor load_module part 4
[~andy/linux] / kernel / module.c
index 6c562828c85c2d3d952c46be2de0b2cb0b3748a9..241bc41dd6bef796ce7402c443d40809d26f5632 100644 (file)
@@ -524,21 +524,21 @@ static char last_unloaded_module[MODULE_NAME_LEN+1];
 EXPORT_TRACEPOINT_SYMBOL(module_get);
 
 /* Init the unload section of the module. */
-static void module_unload_init(struct module *mod)
+static int module_unload_init(struct module *mod)
 {
-       int cpu;
+       mod->refptr = alloc_percpu(struct module_ref);
+       if (!mod->refptr)
+               return -ENOMEM;
 
        INIT_LIST_HEAD(&mod->source_list);
        INIT_LIST_HEAD(&mod->target_list);
-       for_each_possible_cpu(cpu) {
-               per_cpu_ptr(mod->refptr, cpu)->incs = 0;
-               per_cpu_ptr(mod->refptr, cpu)->decs = 0;
-       }
 
        /* Hold reference count during initialization. */
        __this_cpu_write(mod->refptr->incs, 1);
        /* Backwards compatibility macros put refcount during init. */
        mod->waiter = current;
+
+       return 0;
 }
 
 /* Does a already use b? */
@@ -618,6 +618,8 @@ static void module_unload_free(struct module *mod)
                kfree(use);
        }
        mutex_unlock(&module_mutex);
+
+       free_percpu(mod->refptr);
 }
 
 #ifdef CONFIG_MODULE_FORCE_UNLOAD
@@ -891,8 +893,9 @@ int ref_module(struct module *a, struct module *b)
 }
 EXPORT_SYMBOL_GPL(ref_module);
 
-static inline void module_unload_init(struct module *mod)
+static inline int module_unload_init(struct module *mod)
 {
+       return 0;
 }
 #endif /* CONFIG_MODULE_UNLOAD */
 
@@ -1565,10 +1568,7 @@ static void free_module(struct module *mod)
        module_free(mod, mod->module_init);
        kfree(mod->args);
        percpu_modfree(mod);
-#if defined(CONFIG_MODULE_UNLOAD)
-       if (mod->refptr)
-               free_percpu(mod->refptr);
-#endif
+
        /* Free lock-classes: */
        lockdep_free_key_range(mod->module_core, mod->core_size);
 
@@ -1809,7 +1809,7 @@ static char *next_string(char *string, unsigned long *secsize)
        return string;
 }
 
-static char *get_modinfo(Elf_Shdr *sechdrs,
+static char *get_modinfo(const Elf_Shdr *sechdrs,
                         unsigned int info,
                         const char *tag)
 {
@@ -2088,7 +2088,8 @@ static void *module_alloc_update_bounds(unsigned long size)
 
 #ifdef CONFIG_DEBUG_KMEMLEAK
 static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
-                                Elf_Shdr *sechdrs, char *secstrings)
+                                const Elf_Shdr *sechdrs,
+                                const char *secstrings)
 {
        unsigned int i;
 
@@ -2108,11 +2109,208 @@ static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
 }
 #else
 static inline void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
-                                       Elf_Shdr *sechdrs, char *secstrings)
+                                       Elf_Shdr *sechdrs,
+                                       const char *secstrings)
 {
 }
 #endif
 
+static int copy_and_check(Elf_Ehdr **hdrp,
+                         const void __user *umod, unsigned long len)
+{
+       int err;
+       Elf_Ehdr *hdr;
+
+       if (len < sizeof(*hdr))
+               return -ENOEXEC;
+
+       /* Suck in entire file: we'll want most of it. */
+       /* vmalloc barfs on "unusual" numbers.  Check here */
+       if (len > 64 * 1024 * 1024 || (hdr = *hdrp = vmalloc(len)) == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(hdr, umod, len) != 0) {
+               err = -EFAULT;
+               goto free_hdr;
+       }
+
+       /* Sanity checks against insmoding binaries or wrong arch,
+          weird elf version */
+       if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
+           || hdr->e_type != ET_REL
+           || !elf_check_arch(hdr)
+           || hdr->e_shentsize != sizeof(Elf_Shdr)) {
+               err = -ENOEXEC;
+               goto free_hdr;
+       }
+
+       if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
+               err = -ENOEXEC;
+               goto free_hdr;
+       }
+       return 0;
+
+free_hdr:
+       vfree(hdr);
+       return err;
+}
+
+static int check_modinfo(struct module *mod,
+                        const Elf_Shdr *sechdrs,
+                        unsigned int infoindex, unsigned int versindex)
+{
+       const char *modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
+       int err;
+
+       /* This is allowed: modprobe --force will invalidate it. */
+       if (!modmagic) {
+               err = try_to_force_load(mod, "bad vermagic");
+               if (err)
+                       return err;
+       } else if (!same_magic(modmagic, vermagic, versindex)) {
+               printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
+                      mod->name, modmagic, vermagic);
+               return -ENOEXEC;
+       }
+
+       if (get_modinfo(sechdrs, infoindex, "staging")) {
+               add_taint_module(mod, TAINT_CRAP);
+               printk(KERN_WARNING "%s: module is from the staging directory,"
+                      " the quality is unknown, you have been warned.\n",
+                      mod->name);
+       }
+       return 0;
+}
+
+static void find_module_sections(struct module *mod, Elf_Ehdr *hdr,
+                                Elf_Shdr *sechdrs, const char *secstrings)
+{
+       mod->kp = section_objs(hdr, sechdrs, secstrings, "__param",
+                              sizeof(*mod->kp), &mod->num_kp);
+       mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab",
+                                sizeof(*mod->syms), &mod->num_syms);
+       mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab");
+       mod->gpl_syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab_gpl",
+                                    sizeof(*mod->gpl_syms),
+                                    &mod->num_gpl_syms);
+       mod->gpl_crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab_gpl");
+       mod->gpl_future_syms = section_objs(hdr, sechdrs, secstrings,
+                                           "__ksymtab_gpl_future",
+                                           sizeof(*mod->gpl_future_syms),
+                                           &mod->num_gpl_future_syms);
+       mod->gpl_future_crcs = section_addr(hdr, sechdrs, secstrings,
+                                           "__kcrctab_gpl_future");
+
+#ifdef CONFIG_UNUSED_SYMBOLS
+       mod->unused_syms = section_objs(hdr, sechdrs, secstrings,
+                                       "__ksymtab_unused",
+                                       sizeof(*mod->unused_syms),
+                                       &mod->num_unused_syms);
+       mod->unused_crcs = section_addr(hdr, sechdrs, secstrings,
+                                       "__kcrctab_unused");
+       mod->unused_gpl_syms = section_objs(hdr, sechdrs, secstrings,
+                                           "__ksymtab_unused_gpl",
+                                           sizeof(*mod->unused_gpl_syms),
+                                           &mod->num_unused_gpl_syms);
+       mod->unused_gpl_crcs = section_addr(hdr, sechdrs, secstrings,
+                                           "__kcrctab_unused_gpl");
+#endif
+#ifdef CONFIG_CONSTRUCTORS
+       mod->ctors = section_objs(hdr, sechdrs, secstrings, ".ctors",
+                                 sizeof(*mod->ctors), &mod->num_ctors);
+#endif
+
+#ifdef CONFIG_TRACEPOINTS
+       mod->tracepoints = section_objs(hdr, sechdrs, secstrings,
+                                       "__tracepoints",
+                                       sizeof(*mod->tracepoints),
+                                       &mod->num_tracepoints);
+#endif
+#ifdef CONFIG_EVENT_TRACING
+       mod->trace_events = section_objs(hdr, sechdrs, secstrings,
+                                        "_ftrace_events",
+                                        sizeof(*mod->trace_events),
+                                        &mod->num_trace_events);
+       /*
+        * This section contains pointers to allocated objects in the trace
+        * code and not scanning it leads to false positives.
+        */
+       kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
+                          mod->num_trace_events, GFP_KERNEL);
+#endif
+#ifdef CONFIG_FTRACE_MCOUNT_RECORD
+       /* sechdrs[0].sh_size is always zero */
+       mod->ftrace_callsites = section_objs(hdr, sechdrs, secstrings,
+                                            "__mcount_loc",
+                                            sizeof(*mod->ftrace_callsites),
+                                            &mod->num_ftrace_callsites);
+#endif
+}
+
+static struct module *move_module(struct module *mod,
+                                 Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+                                 const char *secstrings, unsigned modindex)
+{
+       int i;
+       void *ptr;
+
+       /* Do the allocs. */
+       ptr = module_alloc_update_bounds(mod->core_size);
+       /*
+        * The pointer to this block is stored in the module structure
+        * which is inside the block. Just mark it as not being a
+        * leak.
+        */
+       kmemleak_not_leak(ptr);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       memset(ptr, 0, mod->core_size);
+       mod->module_core = ptr;
+
+       ptr = module_alloc_update_bounds(mod->init_size);
+       /*
+        * The pointer to this block is stored in the module structure
+        * which is inside the block. This block doesn't need to be
+        * scanned as it contains data and code that will be freed
+        * after the module is initialized.
+        */
+       kmemleak_ignore(ptr);
+       if (!ptr && mod->init_size) {
+               module_free(mod, mod->module_core);
+               return ERR_PTR(-ENOMEM);
+       }
+       memset(ptr, 0, mod->init_size);
+       mod->module_init = ptr;
+
+       /* Transfer each section which specifies SHF_ALLOC */
+       DEBUGP("final section addresses:\n");
+       for (i = 0; i < hdr->e_shnum; i++) {
+               void *dest;
+
+               if (!(sechdrs[i].sh_flags & SHF_ALLOC))
+                       continue;
+
+               if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
+                       dest = mod->module_init
+                               + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
+               else
+                       dest = mod->module_core + sechdrs[i].sh_entsize;
+
+               if (sechdrs[i].sh_type != SHT_NOBITS)
+                       memcpy(dest, (void *)sechdrs[i].sh_addr,
+                              sechdrs[i].sh_size);
+               /* Update sh_addr to point to copy in image. */
+               sechdrs[i].sh_addr = (unsigned long)dest;
+               DEBUGP("\t0x%lx %s\n",
+                      sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
+       }
+       /* Module has been moved. */
+       mod = (void *)sechdrs[modindex].sh_addr;
+       kmemleak_load_module(mod, hdr, sechdrs, secstrings);
+       return mod;
+}
+
 /* Allocate and load the module: note that size of section 0 is always
    zero, and we rely on this for optional sections. */
 static noinline struct module *load_module(void __user *umod,
@@ -2121,15 +2319,13 @@ static noinline struct module *load_module(void __user *umod,
 {
        Elf_Ehdr *hdr;
        Elf_Shdr *sechdrs;
-       char *secstrings, *args, *modmagic, *strtab = NULL;
-       char *staging;
+       char *secstrings, *args, *strtab = NULL;
        unsigned int i;
        unsigned int symindex = 0;
        unsigned int strindex = 0;
        unsigned int modindex, versindex, infoindex, pcpuindex;
        struct module *mod;
-       long err = 0;
-       void *ptr = NULL; /* Stops spurious gcc warning */
+       long err;
        unsigned long symoffs, stroffs, *strmap;
        void __percpu *percpu;
        struct _ddebug *debug = NULL;
@@ -2139,31 +2335,10 @@ static noinline struct module *load_module(void __user *umod,
 
        DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
               umod, len, uargs);
-       if (len < sizeof(*hdr))
-               return ERR_PTR(-ENOEXEC);
-
-       /* Suck in entire file: we'll want most of it. */
-       /* vmalloc barfs on "unusual" numbers.  Check here */
-       if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
-               return ERR_PTR(-ENOMEM);
 
-       if (copy_from_user(hdr, umod, len) != 0) {
-               err = -EFAULT;
-               goto free_hdr;
-       }
-
-       /* Sanity checks against insmoding binaries or wrong arch,
-           weird elf version */
-       if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
-           || hdr->e_type != ET_REL
-           || !elf_check_arch(hdr)
-           || hdr->e_shentsize != sizeof(*sechdrs)) {
-               err = -ENOEXEC;
-               goto free_hdr;
-       }
-
-       if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
-               goto truncated;
+       err = copy_and_check(&hdr, umod, len);
+       if (err)
+               return ERR_PTR(err);
 
        /* Convenience variables */
        sechdrs = (void *)hdr + hdr->e_shoff;
@@ -2223,26 +2398,9 @@ static noinline struct module *load_module(void __user *umod,
                goto free_hdr;
        }
 
-       modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
-       /* This is allowed: modprobe --force will invalidate it. */
-       if (!modmagic) {
-               err = try_to_force_load(mod, "bad vermagic");
-               if (err)
-                       goto free_hdr;
-       } else if (!same_magic(modmagic, vermagic, versindex)) {
-               printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
-                      mod->name, modmagic, vermagic);
-               err = -ENOEXEC;
+       err = check_modinfo(mod, sechdrs, infoindex, versindex);
+       if (err)
                goto free_hdr;
-       }
-
-       staging = get_modinfo(sechdrs, infoindex, "staging");
-       if (staging) {
-               add_taint_module(mod, TAINT_CRAP);
-               printk(KERN_WARNING "%s: module is from the staging directory,"
-                      " the quality is unknown, you have been warned.\n",
-                      mod->name);
-       }
 
        /* Now copy in args */
        args = strndup_user(uargs, ~0UL >> 1);
@@ -2283,70 +2441,17 @@ static noinline struct module *load_module(void __user *umod,
        symoffs = layout_symtab(mod, sechdrs, symindex, strindex, hdr,
                                secstrings, &stroffs, strmap);
 
-       /* Do the allocs. */
-       ptr = module_alloc_update_bounds(mod->core_size);
-       /*
-        * The pointer to this block is stored in the module structure
-        * which is inside the block. Just mark it as not being a
-        * leak.
-        */
-       kmemleak_not_leak(ptr);
-       if (!ptr) {
-               err = -ENOMEM;
+       /* Allocate and move to the final place */
+       mod = move_module(mod, hdr, sechdrs, secstrings, modindex);
+       if (IS_ERR(mod)) {
+               err = PTR_ERR(mod);
                goto free_percpu;
        }
-       memset(ptr, 0, mod->core_size);
-       mod->module_core = ptr;
-
-       ptr = module_alloc_update_bounds(mod->init_size);
-       /*
-        * The pointer to this block is stored in the module structure
-        * which is inside the block. This block doesn't need to be
-        * scanned as it contains data and code that will be freed
-        * after the module is initialized.
-        */
-       kmemleak_ignore(ptr);
-       if (!ptr && mod->init_size) {
-               err = -ENOMEM;
-               goto free_core;
-       }
-       memset(ptr, 0, mod->init_size);
-       mod->module_init = ptr;
-
-       /* Transfer each section which specifies SHF_ALLOC */
-       DEBUGP("final section addresses:\n");
-       for (i = 0; i < hdr->e_shnum; i++) {
-               void *dest;
-
-               if (!(sechdrs[i].sh_flags & SHF_ALLOC))
-                       continue;
-
-               if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
-                       dest = mod->module_init
-                               + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
-               else
-                       dest = mod->module_core + sechdrs[i].sh_entsize;
-
-               if (sechdrs[i].sh_type != SHT_NOBITS)
-                       memcpy(dest, (void *)sechdrs[i].sh_addr,
-                              sechdrs[i].sh_size);
-               /* Update sh_addr to point to copy in image. */
-               sechdrs[i].sh_addr = (unsigned long)dest;
-               DEBUGP("\t0x%lx %s\n", sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
-       }
-       /* Module has been moved. */
-       mod = (void *)sechdrs[modindex].sh_addr;
-       kmemleak_load_module(mod, hdr, sechdrs, secstrings);
 
-#if defined(CONFIG_MODULE_UNLOAD)
-       mod->refptr = alloc_percpu(struct module_ref);
-       if (!mod->refptr) {
-               err = -ENOMEM;
-               goto free_init;
-       }
-#endif
        /* Now we've moved module, initialize linked lists, etc. */
-       module_unload_init(mod);
+       err = module_unload_init(mod);
+       if (err)
+               goto free_init;
 
        /* Set up license info based on the info section */
        set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
@@ -2374,66 +2479,8 @@ static noinline struct module *load_module(void __user *umod,
 
        /* Now we've got everything in the final locations, we can
         * find optional sections. */
-       mod->kp = section_objs(hdr, sechdrs, secstrings, "__param",
-                              sizeof(*mod->kp), &mod->num_kp);
-       mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab",
-                                sizeof(*mod->syms), &mod->num_syms);
-       mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab");
-       mod->gpl_syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab_gpl",
-                                    sizeof(*mod->gpl_syms),
-                                    &mod->num_gpl_syms);
-       mod->gpl_crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab_gpl");
-       mod->gpl_future_syms = section_objs(hdr, sechdrs, secstrings,
-                                           "__ksymtab_gpl_future",
-                                           sizeof(*mod->gpl_future_syms),
-                                           &mod->num_gpl_future_syms);
-       mod->gpl_future_crcs = section_addr(hdr, sechdrs, secstrings,
-                                           "__kcrctab_gpl_future");
-
-#ifdef CONFIG_UNUSED_SYMBOLS
-       mod->unused_syms = section_objs(hdr, sechdrs, secstrings,
-                                       "__ksymtab_unused",
-                                       sizeof(*mod->unused_syms),
-                                       &mod->num_unused_syms);
-       mod->unused_crcs = section_addr(hdr, sechdrs, secstrings,
-                                       "__kcrctab_unused");
-       mod->unused_gpl_syms = section_objs(hdr, sechdrs, secstrings,
-                                           "__ksymtab_unused_gpl",
-                                           sizeof(*mod->unused_gpl_syms),
-                                           &mod->num_unused_gpl_syms);
-       mod->unused_gpl_crcs = section_addr(hdr, sechdrs, secstrings,
-                                           "__kcrctab_unused_gpl");
-#endif
-#ifdef CONFIG_CONSTRUCTORS
-       mod->ctors = section_objs(hdr, sechdrs, secstrings, ".ctors",
-                                 sizeof(*mod->ctors), &mod->num_ctors);
-#endif
+       find_module_sections(mod, hdr, sechdrs, secstrings);
 
-#ifdef CONFIG_TRACEPOINTS
-       mod->tracepoints = section_objs(hdr, sechdrs, secstrings,
-                                       "__tracepoints",
-                                       sizeof(*mod->tracepoints),
-                                       &mod->num_tracepoints);
-#endif
-#ifdef CONFIG_EVENT_TRACING
-       mod->trace_events = section_objs(hdr, sechdrs, secstrings,
-                                        "_ftrace_events",
-                                        sizeof(*mod->trace_events),
-                                        &mod->num_trace_events);
-       /*
-        * This section contains pointers to allocated objects in the trace
-        * code and not scanning it leads to false positives.
-        */
-       kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
-                          mod->num_trace_events, GFP_KERNEL);
-#endif
-#ifdef CONFIG_FTRACE_MCOUNT_RECORD
-       /* sechdrs[0].sh_size is always zero */
-       mod->ftrace_callsites = section_objs(hdr, sechdrs, secstrings,
-                                            "__mcount_loc",
-                                            sizeof(*mod->ftrace_callsites),
-                                            &mod->num_ftrace_callsites);
-#endif
 #ifdef CONFIG_MODVERSIONS
        if ((mod->num_syms && !mod->crcs)
            || (mod->num_gpl_syms && !mod->gpl_crcs)
@@ -2573,12 +2620,8 @@ static noinline struct module *load_module(void __user *umod,
  cleanup:
        free_modinfo(mod);
        module_unload_free(mod);
-#if defined(CONFIG_MODULE_UNLOAD)
-       free_percpu(mod->refptr);
  free_init:
-#endif
        module_free(mod, mod->module_init);
- free_core:
        module_free(mod, mod->module_core);
        /* mod will be freed with core. Don't access it beyond this line! */
  free_percpu: