X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fmodule.c;h=5aad477ddc79c94567c02e9ee704f1eb71fa1b66;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=e7a73bac2d58a4524cca09e85db3a5493bd3f431;hpb=bc77d24c47b89f1e0efed0b8e4be5f8aad102883;p=linux-2.6.git diff --git a/kernel/module.c b/kernel/module.c index e7a73bac2..5aad477dd 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -34,9 +36,11 @@ #include #include #include +#include +#include +#include #include #include -#include #include #if 0 @@ -52,11 +56,8 @@ /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) -#define symbol_is(literal, string) \ - (strcmp(MODULE_SYMBOL_PREFIX literal, (string)) == 0) - /* Protects module list */ -static spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(modlist_lock); /* List of modules, protected by module_mutex AND modlist_lock */ static DECLARE_MUTEX(module_mutex); @@ -93,13 +94,6 @@ static inline int strong_try_module_get(struct module *mod) return try_module_get(mod); } -/* Stub function for modules which don't have an initfn */ -int init_module(void) -{ - return 0; -} -EXPORT_SYMBOL(init_module); - /* A thread that wants to hold a reference to a module only while it * is running can call ths to safely exit. * nfsd and lockd use this. @@ -259,13 +253,18 @@ static inline unsigned int block_size(int val) /* Created by linker magic */ extern char __per_cpu_start[], __per_cpu_end[]; -static void *percpu_modalloc(unsigned long size, unsigned long align) +static void *percpu_modalloc(unsigned long size, unsigned long align, + const char *name) { unsigned long extra; unsigned int i; void *ptr; - BUG_ON(align > SMP_CACHE_BYTES); + if (align > SMP_CACHE_BYTES) { + printk(KERN_WARNING "%s: per-cpu alignment %li > %i\n", + name, align, SMP_CACHE_BYTES); + align = SMP_CACHE_BYTES; + } ptr = __per_cpu_start; for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) { @@ -357,7 +356,8 @@ static int percpu_modinit(void) } __initcall(percpu_modinit); #else /* ... !CONFIG_SMP */ -static inline void *percpu_modalloc(unsigned long size, unsigned long align) +static inline void *percpu_modalloc(unsigned long size, unsigned long align, + const char *name) { return NULL; } @@ -379,23 +379,44 @@ static inline void percpu_modcopy(void *pcpudst, const void *src, } #endif /* CONFIG_SMP */ -static int add_attribute(struct module *mod, struct kernel_param *kp) -{ - struct module_attribute *a; - int retval; +#ifdef CONFIG_MODULE_UNLOAD +#define MODINFO_ATTR(field) \ +static void setup_modinfo_##field(struct module *mod, const char *s) \ +{ \ + mod->field = kstrdup(s, GFP_KERNEL); \ +} \ +static ssize_t show_modinfo_##field(struct module_attribute *mattr, \ + struct module *mod, char *buffer) \ +{ \ + return sprintf(buffer, "%s\n", mod->field); \ +} \ +static int modinfo_##field##_exists(struct module *mod) \ +{ \ + return mod->field != NULL; \ +} \ +static void free_modinfo_##field(struct module *mod) \ +{ \ + kfree(mod->field); \ + mod->field = NULL; \ +} \ +static struct module_attribute modinfo_##field = { \ + .attr = { .name = __stringify(field), .mode = 0444, \ + .owner = THIS_MODULE }, \ + .show = show_modinfo_##field, \ + .setup = setup_modinfo_##field, \ + .test = modinfo_##field##_exists, \ + .free = free_modinfo_##field, \ +}; - a = &mod->mkobj->attr[mod->mkobj->num_attributes]; - a->attr.name = (char *)kp->name; - a->attr.owner = mod; - a->attr.mode = kp->perm; - a->param = kp; - retval = sysfs_create_file(&mod->mkobj->kobj, &a->attr); - if (!retval) - mod->mkobj->num_attributes++; - return retval; -} +MODINFO_ATTR(version); +MODINFO_ATTR(srcversion); + +static struct module_attribute *modinfo_attrs[] = { + &modinfo_version, + &modinfo_srcversion, + NULL, +}; -#ifdef CONFIG_MODULE_UNLOAD /* Init the unload section of the module. */ static void module_unload_init(struct module *mod) { @@ -405,7 +426,7 @@ static void module_unload_init(struct module *mod) for (i = 0; i < NR_CPUS; i++) local_set(&mod->ref[i].count, 0); /* Hold reference count during initialization. */ - local_set(&mod->ref[smp_processor_id()].count, 1); + local_set(&mod->ref[raw_smp_processor_id()].count, 1); /* Backwards compatibility macros put refcount during init. */ mod->waiter = current; } @@ -476,15 +497,15 @@ static void module_unload_free(struct module *mod) } #ifdef CONFIG_MODULE_FORCE_UNLOAD -static inline int try_force(unsigned int flags) +static inline int try_force_unload(unsigned int flags) { int ret = (flags & O_TRUNC); if (ret) - tainted |= TAINT_FORCED_MODULE; + add_taint(TAINT_FORCED_RMMOD); return ret; } #else -static inline int try_force(unsigned int flags) +static inline int try_force_unload(unsigned int flags) { return 0; } @@ -498,13 +519,13 @@ struct stopref }; /* Whole machine is stopped with interrupts off when this runs. */ -static inline int __try_stop_module(void *_sref) +static int __try_stop_module(void *_sref) { struct stopref *sref = _sref; /* If it's not unused, quit unless we are told to block. */ if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) { - if (!(*sref->forced = try_force(sref->flags))) + if (!(*sref->forced = try_force_unload(sref->flags))) return -EWOULDBLOCK; } @@ -533,12 +554,6 @@ EXPORT_SYMBOL(module_refcount); /* This exists whether we can unload or not */ static void free_module(struct module *mod); -/* Stub function for modules which don't have an exitfn */ -void cleanup_module(void) -{ -} -EXPORT_SYMBOL(cleanup_module); - static void wait_for_zero_refcount(struct module *mod) { /* Since we might sleep for some time, drop the semaphore first */ @@ -593,9 +608,9 @@ sys_delete_module(const char __user *name_user, unsigned int flags) } /* If it has an init func, it must have an exit func to unload */ - if ((mod->init != init_module && mod->exit == cleanup_module) + if ((mod->init != NULL && mod->exit == NULL) || mod->unsafe) { - forced = try_force(flags); + forced = try_force_unload(flags); if (!forced) { /* This module can't be removed */ ret = -EBUSY; @@ -608,15 +623,19 @@ sys_delete_module(const char __user *name_user, unsigned int flags) /* Stop the machine so refcounts can't move and disable module. */ ret = try_stop_module(mod, flags, &forced); + if (ret != 0) + goto out; /* Never wait if forced. */ if (!forced && module_refcount(mod) != 0) wait_for_zero_refcount(mod); /* Final destruction now noone is using it. */ - up(&module_mutex); - mod->exit(); - down(&module_mutex); + if (mod->exit != NULL) { + up(&module_mutex); + mod->exit(); + down(&module_mutex); + } free_module(mod); out: @@ -643,7 +662,7 @@ static void print_unload_info(struct seq_file *m, struct module *mod) seq_printf(m, "[unsafe],"); } - if (mod->init != init_module && mod->exit == cleanup_module) { + if (mod->init != NULL && mod->exit == NULL) { printed_something = 1; seq_printf(m, "[permanent],"); } @@ -679,22 +698,17 @@ void symbol_put_addr(void *addr) } EXPORT_SYMBOL_GPL(symbol_put_addr); -static int refcnt_get_fn(char *buffer, struct kernel_param *kp) +static ssize_t show_refcnt(struct module_attribute *mattr, + struct module *mod, char *buffer) { - struct module *mod = container_of(kp, struct module, refcnt_param); - - /* sysfs holds one reference. */ - return sprintf(buffer, "%u", module_refcount(mod)-1); + /* sysfs holds a reference */ + return sprintf(buffer, "%u\n", module_refcount(mod)-1); } -static inline int sysfs_unload_setup(struct module *mod) -{ - mod->refcnt_param.name = "refcnt"; - mod->refcnt_param.perm = 0444; - mod->refcnt_param.get = refcnt_get_fn; - - return add_attribute(mod, &mod->refcnt_param); -} +static struct module_attribute refcnt = { + .attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE }, + .show = show_refcnt, +}; #else /* !CONFIG_MODULE_UNLOAD */ static void print_unload_info(struct seq_file *m, struct module *mod) @@ -715,33 +729,9 @@ static inline int use_module(struct module *a, struct module *b) static inline void module_unload_init(struct module *mod) { } - -asmlinkage long -sys_delete_module(const char *name_user, unsigned int flags) -{ - return -ENOSYS; -} - -static inline int sysfs_unload_setup(struct module *mod) -{ - return 0; -} #endif /* CONFIG_MODULE_UNLOAD */ #ifdef CONFIG_OBSOLETE_MODPARM -static int param_set_byte(const char *val, struct kernel_param *kp) -{ - char *endp; - long l; - - if (!val) return -EINVAL; - l = simple_strtol(val, &endp, 0); - if (endp == val || *endp || ((char)l != l)) - return -EINVAL; - *((char *)kp->arg) = l; - return 0; -} - /* Bounds checking done below */ static int obsparm_copy_string(const char *val, struct kernel_param *kp) { @@ -749,7 +739,7 @@ static int obsparm_copy_string(const char *val, struct kernel_param *kp) return 0; } -int set_obsolete(const char *val, struct kernel_param *kp) +static int set_obsolete(const char *val, struct kernel_param *kp) { unsigned int min, max; unsigned int size, maxsize; @@ -910,7 +900,7 @@ static int check_version(Elf_Shdr *sechdrs, if (!(tainted & TAINT_FORCED_MODULE)) { printk("%s: no version for \"%s\" found: kernel tainted.\n", mod->name, symname); - tainted |= TAINT_FORCED_MODULE; + add_taint(TAINT_FORCED_MODULE); } return 1; } @@ -969,7 +959,6 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs, unsigned long ret; const unsigned long *crc; - spin_lock_irq(&modlist_lock); ret = __find_symbol(name, &owner, &crc, mod->license_gplok); if (ret) { /* use_module can fail due to OOM, or module unloading */ @@ -977,128 +966,209 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs, !use_module(mod, owner)) ret = 0; } - spin_unlock_irq(&modlist_lock); return ret; } -#define to_module_attr(n) container_of(n, struct module_attribute, attr); -static ssize_t module_attr_show(struct kobject *kobj, - struct attribute *attr, - char *buf) +/* + * /sys/module/foo/sections stuff + * J. Corbet + */ +#ifdef CONFIG_KALLSYMS +static ssize_t module_sect_show(struct module_attribute *mattr, + struct module *mod, char *buf) { - int count; - struct module_attribute *attribute = to_module_attr(attr); + struct module_sect_attr *sattr = + container_of(mattr, struct module_sect_attr, mattr); + return sprintf(buf, "0x%lx\n", sattr->address); +} - if (!attribute->param->get) - return -EPERM; +static void add_sect_attrs(struct module *mod, unsigned int nsect, + char *secstrings, Elf_Shdr *sechdrs) +{ + unsigned int nloaded = 0, i, size[2]; + struct module_sect_attrs *sect_attrs; + struct module_sect_attr *sattr; + struct attribute **gattr; + + /* Count loaded sections and allocate structures */ + for (i = 0; i < nsect; i++) + if (sechdrs[i].sh_flags & SHF_ALLOC) + nloaded++; + size[0] = ALIGN(sizeof(*sect_attrs) + + nloaded * sizeof(sect_attrs->attrs[0]), + sizeof(sect_attrs->grp.attrs[0])); + size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]); + if (! (sect_attrs = kmalloc(size[0] + size[1], GFP_KERNEL))) + return; + + /* Setup section attributes. */ + sect_attrs->grp.name = "sections"; + sect_attrs->grp.attrs = (void *)sect_attrs + size[0]; + + sattr = §_attrs->attrs[0]; + gattr = §_attrs->grp.attrs[0]; + for (i = 0; i < nsect; i++) { + if (! (sechdrs[i].sh_flags & SHF_ALLOC)) + continue; + sattr->address = sechdrs[i].sh_addr; + strlcpy(sattr->name, secstrings + sechdrs[i].sh_name, + MODULE_SECT_NAME_LEN); + sattr->mattr.show = module_sect_show; + sattr->mattr.store = NULL; + sattr->mattr.attr.name = sattr->name; + sattr->mattr.attr.owner = mod; + sattr->mattr.attr.mode = S_IRUGO; + *(gattr++) = &(sattr++)->mattr.attr; + } + *gattr = NULL; + + if (sysfs_create_group(&mod->mkobj.kobj, §_attrs->grp)) + goto out; - count = attribute->param->get(buf, attribute->param); - if (count > 0) { - strcat(buf, "\n"); - ++count; - } - return count; + mod->sect_attrs = sect_attrs; + return; + out: + kfree(sect_attrs); } -/* sysfs always hands a nul-terminated string in buf. We rely on that. */ -static ssize_t module_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, size_t len) +static void remove_sect_attrs(struct module *mod) { - int err; - struct module_attribute *attribute = to_module_attr(attr); + if (mod->sect_attrs) { + sysfs_remove_group(&mod->mkobj.kobj, + &mod->sect_attrs->grp); + /* We are positive that no one is using any sect attrs + * at this point. Deallocate immediately. */ + kfree(mod->sect_attrs); + mod->sect_attrs = NULL; + } +} - if (!attribute->param->set) - return -EPERM; - err = attribute->param->set(buf, attribute->param); - if (!err) - return len; - return err; +#else +static inline void add_sect_attrs(struct module *mod, unsigned int nsect, + char *sectstrings, Elf_Shdr *sechdrs) +{ } -static struct sysfs_ops module_sysfs_ops = { - .show = module_attr_show, - .store = module_attr_store, -}; +static inline void remove_sect_attrs(struct module *mod) +{ +} +#endif /* CONFIG_KALLSYMS */ + -static void module_kobj_release(struct kobject *kobj) +#ifdef CONFIG_MODULE_UNLOAD +static inline int module_add_refcnt_attr(struct module *mod) { - kfree(container_of(kobj, struct module_kobject, kobj)); + return sysfs_create_file(&mod->mkobj.kobj, &refcnt.attr); } +static void module_remove_refcnt_attr(struct module *mod) +{ + return sysfs_remove_file(&mod->mkobj.kobj, &refcnt.attr); +} +#else +static inline int module_add_refcnt_attr(struct module *mod) +{ + return 0; +} +static void module_remove_refcnt_attr(struct module *mod) +{ +} +#endif -static struct kobj_type module_ktype = { - .sysfs_ops = &module_sysfs_ops, - .release = &module_kobj_release, -}; -static decl_subsys(module, &module_ktype, NULL); +#ifdef CONFIG_MODULE_UNLOAD +static int module_add_modinfo_attrs(struct module *mod) +{ + struct module_attribute *attr; + int error = 0; + int i; + + for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { + if (!attr->test || + (attr->test && attr->test(mod))) + error = sysfs_create_file(&mod->mkobj.kobj,&attr->attr); + } + return error; +} + +static void module_remove_modinfo_attrs(struct module *mod) +{ + struct module_attribute *attr; + int i; + + for (i = 0; (attr = modinfo_attrs[i]); i++) { + sysfs_remove_file(&mod->mkobj.kobj,&attr->attr); + attr->free(mod); + } +} +#endif static int mod_sysfs_setup(struct module *mod, struct kernel_param *kparam, unsigned int num_params) { - unsigned int i; int err; - /* We overallocate: not every param is in sysfs, and maybe no refcnt */ - mod->mkobj = kmalloc(sizeof(*mod->mkobj) - + sizeof(mod->mkobj->attr[0]) * (num_params+1), - GFP_KERNEL); - if (!mod->mkobj) - return -ENOMEM; - - memset(&mod->mkobj->kobj, 0, sizeof(mod->mkobj->kobj)); - err = kobject_set_name(&mod->mkobj->kobj, mod->name); + memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj)); + err = kobject_set_name(&mod->mkobj.kobj, "%s", mod->name); if (err) goto out; - kobj_set_kset_s(mod->mkobj, module_subsys); - err = kobject_register(&mod->mkobj->kobj); + kobj_set_kset_s(&mod->mkobj, module_subsys); + mod->mkobj.mod = mod; + err = kobject_register(&mod->mkobj.kobj); if (err) goto out; - mod->mkobj->num_attributes = 0; + err = module_add_refcnt_attr(mod); + if (err) + goto out_unreg; - for (i = 0; i < num_params; i++) { - if (kparam[i].perm) { - err = add_attribute(mod, &kparam[i]); - if (err) - goto out_unreg; - } - } - err = sysfs_unload_setup(mod); + err = module_param_sysfs_setup(mod, kparam, num_params); + if (err) + goto out_unreg; + +#ifdef CONFIG_MODULE_UNLOAD + err = module_add_modinfo_attrs(mod); if (err) goto out_unreg; +#endif + return 0; out_unreg: - for (i = 0; i < mod->mkobj->num_attributes; i++) - sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr); - /* Calls module_kobj_release */ - kobject_unregister(&mod->mkobj->kobj); - return err; + kobject_unregister(&mod->mkobj.kobj); out: - kfree(mod->mkobj); return err; } static void mod_kobject_remove(struct module *mod) { - unsigned int i; - for (i = 0; i < mod->mkobj->num_attributes; i++) - sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr); - /* Calls module_kobj_release */ - kobject_unregister(&mod->mkobj->kobj); +#ifdef CONFIG_MODULE_UNLOAD + module_remove_modinfo_attrs(mod); +#endif + module_remove_refcnt_attr(mod); + module_param_sysfs_remove(mod); + + kobject_unregister(&mod->mkobj.kobj); +} + +/* + * unlink the module with the whole machine is stopped with interrupts off + * - this defends against kallsyms not taking locks + */ +static int __unlink_module(void *_mod) +{ + struct module *mod = _mod; + list_del(&mod->list); + return 0; } /* Free a module, remove from lists, etc (must hold module mutex). */ static void free_module(struct module *mod) { /* Delete from various lists */ - spin_lock_irq(&modlist_lock); - list_del(&mod->list); - spin_unlock_irq(&modlist_lock); - + stop_machine_run(__unlink_module, mod, NR_CPUS); + remove_sect_attrs(mod); mod_kobject_remove(mod); /* Arch-specific cleanup. */ @@ -1133,6 +1203,39 @@ void *__symbol_get(const char *symbol) } EXPORT_SYMBOL_GPL(__symbol_get); +/* + * Ensure that an exported symbol [global namespace] does not already exist + * in the Kernel or in some other modules exported symbol table. + */ +static int verify_export_symbols(struct module *mod) +{ + const char *name = NULL; + unsigned long i, ret = 0; + struct module *owner; + const unsigned long *crc; + + for (i = 0; i < mod->num_syms; i++) + if (__find_symbol(mod->syms[i].name, &owner, &crc, 1)) { + name = mod->syms[i].name; + ret = -ENOEXEC; + goto dup; + } + + for (i = 0; i < mod->num_gpl_syms; i++) + if (__find_symbol(mod->gpl_syms[i].name, &owner, &crc, 1)) { + name = mod->gpl_syms[i].name; + ret = -ENOEXEC; + goto dup; + } + +dup: + if (ret) + printk(KERN_ERR "%s: exports duplicate symbol %s (owned by %s)\n", + mod->name, name, module_name(owner)); + + return ret; +} + /* Change all symbols so that sh_value encodes the pointer directly. */ static int simplify_symbols(Elf_Shdr *sechdrs, unsigned int symindex, @@ -1283,7 +1386,7 @@ static void set_license(struct module *mod, const char *license) if (!mod->license_gplok && !(tainted & TAINT_PROPRIETARY_MODULE)) { printk(KERN_WARNING "%s: module license '%s' taints kernel.\n", mod->name, license); - tainted |= TAINT_PROPRIETARY_MODULE; + add_taint(TAINT_PROPRIETARY_MODULE); } } @@ -1321,6 +1424,23 @@ static char *get_modinfo(Elf_Shdr *sechdrs, return NULL; } +#ifdef CONFIG_MODULE_UNLOAD +static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs, + unsigned int infoindex) +{ + struct module_attribute *attr; + int i; + + for (i = 0; (attr = modinfo_attrs[i]); i++) { + if (attr->setup) + attr->setup(mod, + get_modinfo(sechdrs, + infoindex, + attr->attr.name)); + } +} +#endif + #ifdef CONFIG_KALLSYMS int is_exported(const char *name, const struct module *mod) { @@ -1423,6 +1543,7 @@ static struct module *load_module(void __user *umod, long err = 0; void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ struct exception_table_entry *extable; + mm_segment_t old_fs; DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); @@ -1456,9 +1577,6 @@ static struct module *load_module(void __user *umod, secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; - /* And these should exist, but gcc whinges if we don't init them */ - symindex = strindex = 0; - for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type != SHT_NOBITS && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) @@ -1490,6 +1608,13 @@ static struct module *load_module(void __user *umod, } mod = (void *)sechdrs[modindex].sh_addr; + if (symindex == 0) { + printk(KERN_WARNING "%s: module has no symbols (stripped?)\n", + mod->name); + err = -ENOEXEC; + goto free_hdr; + } + /* Optional sections */ exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab"); gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl"); @@ -1519,7 +1644,7 @@ static struct module *load_module(void __user *umod, modmagic = get_modinfo(sechdrs, infoindex, "vermagic"); /* This is allowed: modprobe --force will invalidate it. */ if (!modmagic) { - tainted |= TAINT_FORCED_MODULE; + add_taint(TAINT_FORCED_MODULE); printk(KERN_WARNING "%s: no version magic, tainting kernel.\n", mod->name); } else if (!same_magic(modmagic, vermagic)) { @@ -1545,6 +1670,9 @@ static struct module *load_module(void __user *umod, goto free_mod; } + /* Userspace could have altered the string after the strlen_user() */ + args[arglen - 1] = '\0'; + if (find_module(mod->name)) { err = -EEXIST; goto free_mod; @@ -1560,7 +1688,8 @@ static struct module *load_module(void __user *umod, if (pcpuindex) { /* We have a special allocation for this section. */ percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size, - sechdrs[pcpuindex].sh_addralign); + sechdrs[pcpuindex].sh_addralign, + mod->name); if (!percpu) { err = -ENOMEM; goto free_mod; @@ -1621,6 +1750,16 @@ static struct module *load_module(void __user *umod, /* Set up license info based on the info section */ set_license(mod, get_modinfo(sechdrs, infoindex, "license")); + if (strcmp(mod->name, "ndiswrapper") == 0) + add_taint(TAINT_PROPRIETARY_MODULE); + if (strcmp(mod->name, "driverloader") == 0) + add_taint(TAINT_PROPRIETARY_MODULE); + +#ifdef CONFIG_MODULE_UNLOAD + /* Set up MODINFO_ATTR fields */ + setup_modinfo(mod, sechdrs, infoindex); +#endif + /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, mod); @@ -1642,7 +1781,7 @@ static struct module *load_module(void __user *umod, (mod->num_gpl_syms && !gplcrcindex)) { printk(KERN_WARNING "%s: No versions for exported symbols." " Tainting kernel.\n", mod->name); - tainted |= TAINT_FORCED_MODULE; + add_taint(TAINT_FORCED_MODULE); } #endif @@ -1668,6 +1807,12 @@ static struct module *load_module(void __user *umod, goto cleanup; } + /* Find duplicate symbols */ + err = verify_export_symbols(mod); + + if (err < 0) + goto cleanup; + /* Set up and sort exception table */ mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable); mod->extable = extable = (void *)sechdrs[exindex].sh_addr; @@ -1683,6 +1828,24 @@ static struct module *load_module(void __user *umod, if (err < 0) goto cleanup; + /* flush the icache in correct context */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + + /* + * Flush the instruction cache, since we've played with text. + * Do it before processing of module parameters, so the module + * can provide parameter accessor functions of its own. + */ + if (mod->module_init) + flush_icache_range((unsigned long)mod->module_init, + (unsigned long)mod->module_init + + mod->init_size); + flush_icache_range((unsigned long)mod->module_core, + (unsigned long)mod->module_core + mod->core_size); + + set_fs(old_fs); + mod->args = args; if (obsparmindex) { err = obsolete_params(mod->name, mod->args, @@ -1705,6 +1868,9 @@ static struct module *load_module(void __user *umod, / sizeof(struct kernel_param), NULL); } + if (err < 0) + goto arch_cleanup; + err = mod_sysfs_setup(mod, (struct kernel_param *) sechdrs[setupindex].sh_addr, @@ -1712,6 +1878,7 @@ static struct module *load_module(void __user *umod, / sizeof(struct kernel_param)); if (err < 0) goto arch_cleanup; + add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); /* Get rid of temporary copy */ vfree(hdr); @@ -1733,8 +1900,7 @@ static struct module *load_module(void __user *umod, kfree(args); free_hdr: vfree(hdr); - if (err < 0) return ERR_PTR(err); - else return ptr; + return ERR_PTR(err); truncated: printk(KERN_ERR "Module len %lu truncated\n", len); @@ -1742,6 +1908,17 @@ static struct module *load_module(void __user *umod, goto free_hdr; } +/* + * link the module with the whole machine is stopped with interrupts off + * - this defends against kallsyms not taking locks + */ +static int __link_module(void *_mod) +{ + struct module *mod = _mod; + list_add(&mod->list, &modules); + return 0; +} + /* This is where the real work happens */ asmlinkage long sys_init_module(void __user *umod, @@ -1749,7 +1926,7 @@ sys_init_module(void __user *umod, const char __user *uargs) { struct module *mod; - int ret; + int ret = 0; /* Must have permission */ if (!capable(CAP_SYS_MODULE)) @@ -1766,19 +1943,9 @@ sys_init_module(void __user *umod, return PTR_ERR(mod); } - /* Flush the instruction cache, since we've played with text */ - if (mod->module_init) - flush_icache_range((unsigned long)mod->module_init, - (unsigned long)mod->module_init - + mod->init_size); - flush_icache_range((unsigned long)mod->module_core, - (unsigned long)mod->module_core + mod->core_size); - /* Now sew it into the lists. They won't access us, since strong_try_module_get() will fail. */ - spin_lock_irq(&modlist_lock); - list_add(&mod->list, &modules); - spin_unlock_irq(&modlist_lock); + stop_machine_run(__link_module, mod, NR_CPUS); /* Drop lock so they can recurse */ up(&module_mutex); @@ -1788,12 +1955,13 @@ sys_init_module(void __user *umod, up(¬ify_mutex); /* Start the module */ - ret = mod->init(); + if (mod->init != NULL) + ret = mod->init(); if (ret < 0) { /* Init routine failed: abort. Try to protect us from buggy refcounters. */ mod->state = MODULE_STATE_GOING; - synchronize_kernel(); + synchronize_sched(); if (mod->unsafe) printk(KERN_ERR "%s: module is now stuck!\n", mod->name); @@ -1826,6 +1994,16 @@ static inline int within(unsigned long addr, void *start, unsigned long size) } #ifdef CONFIG_KALLSYMS +/* + * This ignores the intensely annoying "mapping symbols" found + * in ARM ELF files: $a, $t and $d. + */ +static inline int is_arm_mapping_symbol(const char *str) +{ + return str[0] == '$' && strchr("atd", str[1]) + && (str[2] == '\0' || str[2] == '.'); +} + static const char *get_ksymbol(struct module *mod, unsigned long addr, unsigned long *size, @@ -1850,11 +2028,13 @@ static const char *get_ksymbol(struct module *mod, * and inserted at a whim. */ if (mod->symtab[i].st_value <= addr && mod->symtab[i].st_value > mod->symtab[best].st_value - && *(mod->strtab + mod->symtab[i].st_name) != '\0' ) + && *(mod->strtab + mod->symtab[i].st_name) != '\0' + && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name)) best = i; if (mod->symtab[i].st_value > addr && mod->symtab[i].st_value < nextval - && *(mod->strtab + mod->symtab[i].st_name) != '\0') + && *(mod->strtab + mod->symtab[i].st_name) != '\0' + && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name)) nextval = mod->symtab[i].st_value; } @@ -1915,7 +2095,8 @@ static unsigned long mod_find_symname(struct module *mod, const char *name) unsigned int i; for (i = 0; i < mod->num_symtab; i++) - if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0) + if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0 && + mod->symtab[i].st_info != 'U') return mod->symtab[i].st_value; return 0; } @@ -2029,7 +2210,7 @@ const struct exception_table_entry *search_module_extables(unsigned long addr) } /* Is this a valid kernel address? We don't grab the lock: we are oopsing. */ -struct module *module_text_address(unsigned long addr) +struct module *__module_text_address(unsigned long addr) { struct module *mod; @@ -2040,6 +2221,18 @@ struct module *module_text_address(unsigned long addr) return NULL; } +struct module *module_text_address(unsigned long addr) +{ + struct module *mod; + unsigned long flags; + + spin_lock_irqsave(&modlist_lock, flags); + mod = __module_text_address(addr); + spin_unlock_irqrestore(&modlist_lock, flags); + + return mod; +} + /* Don't grab lock, we're oopsing. */ void print_modules(void) { @@ -2051,14 +2244,26 @@ void print_modules(void) printk("\n"); } +void module_add_driver(struct module *mod, struct device_driver *drv) +{ + if (!mod || !drv) + return; + + /* Don't check return code; this call is idempotent */ + sysfs_create_link(&drv->kobj, &mod->mkobj.kobj, "module"); +} +EXPORT_SYMBOL(module_add_driver); + +void module_remove_driver(struct device_driver *drv) +{ + if (!drv) + return; + sysfs_remove_link(&drv->kobj, "module"); +} +EXPORT_SYMBOL(module_remove_driver); + #ifdef CONFIG_MODVERSIONS /* Generate the signature for struct module here, too, for modversions. */ void struct_module(struct module *mod) { return; } EXPORT_SYMBOL(struct_module); #endif - -static int __init modules_init(void) -{ - return subsystem_register(&module_subsys); -} -__initcall(modules_init);