Surprising number of fixes this merge window :(
First two are minor fallout from the param rework which went in this merge window. Next three are a series which fixes a longstanding (but never previously reported and unlikely , so no CC stable) race between kallsyms and freeing the init section. Finally, a minor cleanup as our module refcount will now be -1 during unload. Thanks, Rusty. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUwEmwAAoJENkgDmzRrbjx77kP/1cNQR2eG2sBwokg3q0tvHnQ IKqEXErW7NvxRa+RAMEmy2uQoGt6+uNklAbtyJEYM9oR1NieFbPi2yrt9Xn5SAXS Brp1S8WYBMilA3W3o6I0trFDRWHdpdtkKIQwLWgJNSEWjbTXh8bSwp/2X1rlOPyI ZmphCMOQMU2/uFEyJhTz1WMEV8eVXiRLN8OxSkPxToxdZoGln2U8IBCCCJC9OG+f Cf3eMgEcNdEXNcPKqr11NIcHkAx6M6qI/eMDOqk151PslHa8lbis6di9Z87aE0ps i8PyrkJGTmgM9cCjXwE8deNseeCmuKYlbPIF+NoxcqtvZstfaMrISwTIEuzV4JHi p13YhDxy4XiC3H6pKHub/jo7UCl+wWtFh9SqpqGgduFX/p6FtUHQJm0S0X/DFFZt C+2MFVSe6HRHE8B7bFz86+619Qd/rU7+806CLCE+NbYlYAKIBYKzWt/bml6VH3RJ OjwXhQqmznWhJjsfD3BUUUpZpHijmylI9gAe2F1oErb8YjRU6gIm7P8hlkOzD7AS TfGHPFq2raQcfAiGdVmvkbvvhvYZXnB3WVsAexrYoqrT9I8eEfRI+7SkL75MLR2E ikzhJS3SHkAUAd7fUVMt7xMwh0jmhsPjWCCqc13m6UUFoXhTaDgKgPGftltN0bI2 g85+enZ3/eca6xh/KxvW =Kf9b -----END PGP SIGNATURE----- Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux Pull module and param fixes from Rusty Russell: "Surprising number of fixes this merge window :( The first two are minor fallout from the param rework which went in this merge window. The next three are a series which fixes a longstanding (but never previously reported and unlikely , so no CC stable) race between kallsyms and freeing the init section. Finally, a minor cleanup as our module refcount will now be -1 during unload" * tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: module: make module_refcount() a signed integer. module: fix race in kallsyms resolution during module load success. module: remove mod arg from module_free, rename module_memfree(). module_arch_freeing_init(): new hook for archs before module->module_init freed. param: fix uninitialized read with CONFIG_DEBUG_LOCK_ALLOC param: initialize store function to NULL if not available.
This commit is contained in:
commit
193934123c
|
@ -19,12 +19,10 @@
|
|||
#include <linux/moduleloader.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
void module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
vfree(mod->arch.syminfo);
|
||||
mod->arch.syminfo = NULL;
|
||||
|
||||
vfree(module_region);
|
||||
}
|
||||
|
||||
static inline int check_rela(Elf32_Rela *rela, struct module *module,
|
||||
|
@ -291,12 +289,3 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
||||
struct module *module)
|
||||
{
|
||||
vfree(module->arch.syminfo);
|
||||
module->arch.syminfo = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ void *module_alloc(unsigned long size)
|
|||
}
|
||||
|
||||
/* Free memory returned from module_alloc */
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
void module_memfree(void *module_region)
|
||||
{
|
||||
kfree(module_region);
|
||||
}
|
||||
|
|
|
@ -305,14 +305,12 @@ plt_target (struct plt_entry *plt)
|
|||
#endif /* !USE_BRL */
|
||||
|
||||
void
|
||||
module_free (struct module *mod, void *module_region)
|
||||
module_arch_freeing_init (struct module *mod)
|
||||
{
|
||||
if (mod && mod->arch.init_unw_table &&
|
||||
module_region == mod->module_init) {
|
||||
if (mod->arch.init_unw_table) {
|
||||
unw_remove_unwind_table(mod->arch.init_unw_table);
|
||||
mod->arch.init_unw_table = NULL;
|
||||
}
|
||||
vfree(module_region);
|
||||
}
|
||||
|
||||
/* Have we already seen one of these relocations? */
|
||||
|
|
|
@ -1388,7 +1388,7 @@ out:
|
|||
void bpf_jit_free(struct bpf_prog *fp)
|
||||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
module_memfree(fp->bpf_func);
|
||||
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ void *module_alloc(unsigned long size)
|
|||
}
|
||||
|
||||
/* Free memory returned from module_alloc */
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
void module_memfree(void *module_region)
|
||||
{
|
||||
kfree(module_region);
|
||||
}
|
||||
|
|
|
@ -298,14 +298,10 @@ static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Free memory returned from module_alloc */
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
void module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
kfree(mod->arch.section);
|
||||
mod->arch.section = NULL;
|
||||
|
||||
vfree(module_region);
|
||||
}
|
||||
|
||||
/* Additional bytes needed in front of individual sections */
|
||||
|
|
|
@ -699,7 +699,7 @@ out:
|
|||
void bpf_jit_free(struct bpf_prog *fp)
|
||||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
module_memfree(fp->bpf_func);
|
||||
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -55,14 +55,10 @@ void *module_alloc(unsigned long size)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Free memory returned from module_alloc */
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
void module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
if (mod) {
|
||||
vfree(mod->arch.syminfo);
|
||||
mod->arch.syminfo = NULL;
|
||||
}
|
||||
vfree(module_region);
|
||||
vfree(mod->arch.syminfo);
|
||||
mod->arch.syminfo = NULL;
|
||||
}
|
||||
|
||||
static void check_rela(Elf_Rela *rela, struct module *me)
|
||||
|
|
|
@ -776,7 +776,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf];
|
|||
if (unlikely(proglen + ilen > oldproglen)) {
|
||||
pr_err("bpb_jit_compile fatal error\n");
|
||||
kfree(addrs);
|
||||
module_free(NULL, image);
|
||||
module_memfree(image);
|
||||
return;
|
||||
}
|
||||
memcpy(image + proglen, temp, ilen);
|
||||
|
@ -822,7 +822,7 @@ out:
|
|||
void bpf_jit_free(struct bpf_prog *fp)
|
||||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
module_memfree(fp->bpf_func);
|
||||
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ error:
|
|||
|
||||
|
||||
/* Free memory returned from module_alloc */
|
||||
void module_free(struct module *mod, void *module_region)
|
||||
void module_memfree(void *module_region)
|
||||
{
|
||||
vfree(module_region);
|
||||
|
||||
|
@ -83,7 +83,7 @@ void module_free(struct module *mod, void *module_region)
|
|||
0, 0, 0, NULL, NULL, 0);
|
||||
|
||||
/*
|
||||
* FIXME: If module_region == mod->module_init, trim exception
|
||||
* FIXME: Add module_arch_freeing_init to trim exception
|
||||
* table entries.
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -674,7 +674,7 @@ static inline void *alloc_tramp(unsigned long size)
|
|||
}
|
||||
static inline void tramp_free(void *tramp)
|
||||
{
|
||||
module_free(NULL, tramp);
|
||||
module_memfree(tramp);
|
||||
}
|
||||
#else
|
||||
/* Trampolines can only be created if modules are supported */
|
||||
|
|
|
@ -444,7 +444,7 @@ extern void __module_put_and_exit(struct module *mod, long code)
|
|||
#define module_put_and_exit(code) __module_put_and_exit(THIS_MODULE, code)
|
||||
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
unsigned long module_refcount(struct module *mod);
|
||||
int module_refcount(struct module *mod);
|
||||
void __symbol_put(const char *symbol);
|
||||
#define symbol_put(x) __symbol_put(VMLINUX_SYMBOL_STR(x))
|
||||
void symbol_put_addr(void *addr);
|
||||
|
|
|
@ -26,7 +26,7 @@ unsigned int arch_mod_section_prepend(struct module *mod, unsigned int section);
|
|||
void *module_alloc(unsigned long size);
|
||||
|
||||
/* Free memory returned from module_alloc. */
|
||||
void module_free(struct module *mod, void *module_region);
|
||||
void module_memfree(void *module_region);
|
||||
|
||||
/*
|
||||
* Apply the given relocation to the (simplified) ELF. Return -error
|
||||
|
@ -82,4 +82,6 @@ int module_finalize(const Elf_Ehdr *hdr,
|
|||
/* Any cleanup needed when module leaves. */
|
||||
void module_arch_cleanup(struct module *mod);
|
||||
|
||||
/* Any cleanup before freeing mod->module_init */
|
||||
void module_arch_freeing_init(struct module *mod);
|
||||
#endif
|
||||
|
|
|
@ -163,7 +163,7 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
|
|||
|
||||
void bpf_jit_binary_free(struct bpf_binary_header *hdr)
|
||||
{
|
||||
module_free(NULL, hdr);
|
||||
module_memfree(hdr);
|
||||
}
|
||||
#endif /* CONFIG_BPF_JIT */
|
||||
|
||||
|
|
|
@ -2023,7 +2023,7 @@ static int kdb_lsmod(int argc, const char **argv)
|
|||
kdb_printf("%-20s%8u 0x%p ", mod->name,
|
||||
mod->core_size, (void *)mod);
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
kdb_printf("%4ld ", module_refcount(mod));
|
||||
kdb_printf("%4d ", module_refcount(mod));
|
||||
#endif
|
||||
if (mod->state == MODULE_STATE_GOING)
|
||||
kdb_printf(" (Unloading)");
|
||||
|
|
|
@ -127,7 +127,7 @@ static void *alloc_insn_page(void)
|
|||
|
||||
static void free_insn_page(void *page)
|
||||
{
|
||||
module_free(NULL, page);
|
||||
module_memfree(page);
|
||||
}
|
||||
|
||||
struct kprobe_insn_cache kprobe_insn_slots = {
|
||||
|
|
|
@ -772,9 +772,18 @@ static int try_stop_module(struct module *mod, int flags, int *forced)
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned long module_refcount(struct module *mod)
|
||||
/**
|
||||
* module_refcount - return the refcount or -1 if unloading
|
||||
*
|
||||
* @mod: the module we're checking
|
||||
*
|
||||
* Returns:
|
||||
* -1 if the module is in the process of unloading
|
||||
* otherwise the number of references in the kernel to the module
|
||||
*/
|
||||
int module_refcount(struct module *mod)
|
||||
{
|
||||
return (unsigned long)atomic_read(&mod->refcnt) - MODULE_REF_BASE;
|
||||
return atomic_read(&mod->refcnt) - MODULE_REF_BASE;
|
||||
}
|
||||
EXPORT_SYMBOL(module_refcount);
|
||||
|
||||
|
@ -856,7 +865,7 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)
|
|||
struct module_use *use;
|
||||
int printed_something = 0;
|
||||
|
||||
seq_printf(m, " %lu ", module_refcount(mod));
|
||||
seq_printf(m, " %i ", module_refcount(mod));
|
||||
|
||||
/*
|
||||
* Always include a trailing , so userspace can differentiate
|
||||
|
@ -908,7 +917,7 @@ EXPORT_SYMBOL_GPL(symbol_put_addr);
|
|||
static ssize_t show_refcnt(struct module_attribute *mattr,
|
||||
struct module_kobject *mk, char *buffer)
|
||||
{
|
||||
return sprintf(buffer, "%lu\n", module_refcount(mk->mod));
|
||||
return sprintf(buffer, "%i\n", module_refcount(mk->mod));
|
||||
}
|
||||
|
||||
static struct module_attribute modinfo_refcnt =
|
||||
|
@ -1795,7 +1804,7 @@ static void unset_module_core_ro_nx(struct module *mod) { }
|
|||
static void unset_module_init_ro_nx(struct module *mod) { }
|
||||
#endif
|
||||
|
||||
void __weak module_free(struct module *mod, void *module_region)
|
||||
void __weak module_memfree(void *module_region)
|
||||
{
|
||||
vfree(module_region);
|
||||
}
|
||||
|
@ -1804,6 +1813,10 @@ void __weak module_arch_cleanup(struct module *mod)
|
|||
{
|
||||
}
|
||||
|
||||
void __weak module_arch_freeing_init(struct module *mod)
|
||||
{
|
||||
}
|
||||
|
||||
/* Free a module, remove from lists, etc. */
|
||||
static void free_module(struct module *mod)
|
||||
{
|
||||
|
@ -1841,7 +1854,8 @@ static void free_module(struct module *mod)
|
|||
|
||||
/* This may be NULL, but that's OK */
|
||||
unset_module_init_ro_nx(mod);
|
||||
module_free(mod, mod->module_init);
|
||||
module_arch_freeing_init(mod);
|
||||
module_memfree(mod->module_init);
|
||||
kfree(mod->args);
|
||||
percpu_modfree(mod);
|
||||
|
||||
|
@ -1850,7 +1864,7 @@ static void free_module(struct module *mod)
|
|||
|
||||
/* Finally, free the core (containing the module structure) */
|
||||
unset_module_core_ro_nx(mod);
|
||||
module_free(mod, mod->module_core);
|
||||
module_memfree(mod->module_core);
|
||||
|
||||
#ifdef CONFIG_MPU
|
||||
update_protections(current->mm);
|
||||
|
@ -2785,7 +2799,7 @@ static int move_module(struct module *mod, struct load_info *info)
|
|||
*/
|
||||
kmemleak_ignore(ptr);
|
||||
if (!ptr) {
|
||||
module_free(mod, mod->module_core);
|
||||
module_memfree(mod->module_core);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ptr, 0, mod->init_size);
|
||||
|
@ -2930,8 +2944,9 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
|
|||
static void module_deallocate(struct module *mod, struct load_info *info)
|
||||
{
|
||||
percpu_modfree(mod);
|
||||
module_free(mod, mod->module_init);
|
||||
module_free(mod, mod->module_core);
|
||||
module_arch_freeing_init(mod);
|
||||
module_memfree(mod->module_init);
|
||||
module_memfree(mod->module_core);
|
||||
}
|
||||
|
||||
int __weak module_finalize(const Elf_Ehdr *hdr,
|
||||
|
@ -2983,10 +2998,31 @@ static void do_mod_ctors(struct module *mod)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* For freeing module_init on success, in case kallsyms traversing */
|
||||
struct mod_initfree {
|
||||
struct rcu_head rcu;
|
||||
void *module_init;
|
||||
};
|
||||
|
||||
static void do_free_init(struct rcu_head *head)
|
||||
{
|
||||
struct mod_initfree *m = container_of(head, struct mod_initfree, rcu);
|
||||
module_memfree(m->module_init);
|
||||
kfree(m);
|
||||
}
|
||||
|
||||
/* This is where the real work happens */
|
||||
static int do_init_module(struct module *mod)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mod_initfree *freeinit;
|
||||
|
||||
freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
|
||||
if (!freeinit) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
freeinit->module_init = mod->module_init;
|
||||
|
||||
/*
|
||||
* We want to find out whether @mod uses async during init. Clear
|
||||
|
@ -2999,18 +3035,7 @@ static int do_init_module(struct module *mod)
|
|||
if (mod->init != NULL)
|
||||
ret = do_one_initcall(mod->init);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Init routine failed: abort. Try to protect us from
|
||||
* buggy refcounters.
|
||||
*/
|
||||
mod->state = MODULE_STATE_GOING;
|
||||
synchronize_sched();
|
||||
module_put(mod);
|
||||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_GOING, mod);
|
||||
free_module(mod);
|
||||
wake_up_all(&module_wq);
|
||||
return ret;
|
||||
goto fail_free_freeinit;
|
||||
}
|
||||
if (ret > 0) {
|
||||
pr_warn("%s: '%s'->init suspiciously returned %d, it should "
|
||||
|
@ -3055,15 +3080,35 @@ static int do_init_module(struct module *mod)
|
|||
mod->strtab = mod->core_strtab;
|
||||
#endif
|
||||
unset_module_init_ro_nx(mod);
|
||||
module_free(mod, mod->module_init);
|
||||
module_arch_freeing_init(mod);
|
||||
mod->module_init = NULL;
|
||||
mod->init_size = 0;
|
||||
mod->init_ro_size = 0;
|
||||
mod->init_text_size = 0;
|
||||
/*
|
||||
* We want to free module_init, but be aware that kallsyms may be
|
||||
* walking this with preempt disabled. In all the failure paths,
|
||||
* we call synchronize_rcu/synchronize_sched, but we don't want
|
||||
* to slow down the success path, so use actual RCU here.
|
||||
*/
|
||||
call_rcu(&freeinit->rcu, do_free_init);
|
||||
mutex_unlock(&module_mutex);
|
||||
wake_up_all(&module_wq);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free_freeinit:
|
||||
kfree(freeinit);
|
||||
fail:
|
||||
/* Try to protect us from buggy refcounters. */
|
||||
mod->state = MODULE_STATE_GOING;
|
||||
synchronize_sched();
|
||||
module_put(mod);
|
||||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_GOING, mod);
|
||||
free_module(mod);
|
||||
wake_up_all(&module_wq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int may_init_module(void)
|
||||
|
|
|
@ -642,12 +642,15 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,
|
|||
mk->mp->grp.attrs = new_attrs;
|
||||
|
||||
/* Tack new one on the end. */
|
||||
memset(&mk->mp->attrs[mk->mp->num], 0, sizeof(mk->mp->attrs[0]));
|
||||
sysfs_attr_init(&mk->mp->attrs[mk->mp->num].mattr.attr);
|
||||
mk->mp->attrs[mk->mp->num].param = kp;
|
||||
mk->mp->attrs[mk->mp->num].mattr.show = param_attr_show;
|
||||
/* Do not allow runtime DAC changes to make param writable. */
|
||||
if ((kp->perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
|
||||
mk->mp->attrs[mk->mp->num].mattr.store = param_attr_store;
|
||||
else
|
||||
mk->mp->attrs[mk->mp->num].mattr.store = NULL;
|
||||
mk->mp->attrs[mk->mp->num].mattr.attr.name = (char *)name;
|
||||
mk->mp->attrs[mk->mp->num].mattr.attr.mode = kp->perm;
|
||||
mk->mp->num++;
|
||||
|
|
Loading…
Reference in New Issue