|
@@ -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)
|