]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
param: fix charp parameters set via sysfs
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 31 Mar 2009 19:05:29 +0000 (13:05 -0600)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 31 Mar 2009 02:35:30 +0000 (13:05 +1030)
Impact: fix crash on reading from /sys/module/.../ieee80211_default_rc_algo

The module_param type "charp" simply sets a char * pointer in the
module to the parameter in the commandline string: this is why we keep
the (mangled) module command line around.  But when set via sysfs (as
about 11 charp parameters can be) this memory is freed on the way
out of the write().  Future reads hit random mem.

So we kstrdup instead: we have to check we're not in early commandline
parsing, and we have to note when we've used it so we can reliably
kfree the parameter when it's next overwritten, and also on module
unload.

(Thanks to Randy Dunlap for CONFIG_SYSFS=n fixes)

Reported-by: Sitsofe Wheeler <sitsofe@yahoo.com>
Diagnosed-by: Frederic Weisbecker <fweisbec@gmail.com>
Tested-by: Frederic Weisbecker <fweisbec@gmail.com>
Tested-by: Christof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
include/linux/module.h
include/linux/moduleparam.h
kernel/module.c
kernel/params.c

index 145a75528cc1868c275bbbcfacb97ab0124eab46..08e5e75d6122a1df7ceae500b6deb669f38c5431 100644 (file)
@@ -248,6 +248,10 @@ struct module
        const unsigned long *crcs;
        unsigned int num_syms;
 
+       /* Kernel parameters. */
+       struct kernel_param *kp;
+       unsigned int num_kp;
+
        /* GPL-only exported symbols. */
        unsigned int num_gpl_syms;
        const struct kernel_symbol *gpl_syms;
index e4af3399ef48ae862cdadd95832a96ac1bca2ffe..a4f0b931846c25e2e0db59e863490287f62d4b63 100644 (file)
@@ -138,6 +138,16 @@ extern int parse_args(const char *name,
                      unsigned num,
                      int (*unknown)(char *param, char *val));
 
+/* Called by module remove. */
+#ifdef CONFIG_SYSFS
+extern void destroy_params(const struct kernel_param *params, unsigned num);
+#else
+static inline void destroy_params(const struct kernel_param *params,
+                                 unsigned num)
+{
+}
+#endif /* !CONFIG_SYSFS */
+
 /* All the helper functions */
 /* The macros to do compile-time type checking stolen from Jakub
    Jelinek, who IIRC came up with this idea for the 2.4 module init code. */
index f77ac320d0b51d021b52ba4c48dba5680e7c4d01..b862fdb6a3723e4f82aaadb49aa10fbc2f881877 100644 (file)
@@ -1491,6 +1491,9 @@ static void free_module(struct module *mod)
        /* Module unload stuff */
        module_unload_free(mod);
 
+       /* Free any allocated parameters. */
+       destroy_params(mod->kp, mod->num_kp);
+
        /* release any pointers to mcount in this module */
        ftrace_release(mod->module_core, mod->core_size);
 
@@ -1898,8 +1901,7 @@ static noinline struct module *load_module(void __user *umod,
        unsigned int symindex = 0;
        unsigned int strindex = 0;
        unsigned int modindex, versindex, infoindex, pcpuindex;
-       unsigned int num_kp, num_mcount;
-       struct kernel_param *kp;
+       unsigned int num_mcount;
        struct module *mod;
        long err = 0;
        void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
@@ -2144,8 +2146,8 @@ static noinline struct module *load_module(void __user *umod,
 
        /* Now we've got everything in the final locations, we can
         * find optional sections. */
-       kp = section_objs(hdr, sechdrs, secstrings, "__param", sizeof(*kp),
-                         &num_kp);
+       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");
@@ -2291,11 +2293,11 @@ static noinline struct module *load_module(void __user *umod,
         */
        list_add_rcu(&mod->list, &modules);
 
-       err = parse_args(mod->name, mod->args, kp, num_kp, NULL);
+       err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
        if (err < 0)
                goto unlink;
 
-       err = mod_sysfs_setup(mod, kp, num_kp);
+       err = mod_sysfs_setup(mod, mod->kp, mod->num_kp);
        if (err < 0)
                goto unlink;
        add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
index a1e3025b19a9aed003967a0a9e2ff3d05456428b..de273ec85bd2a1a9d9ac6077ddb3078f0d8d04ef 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 
+/* We abuse the high bits of "perm" to record whether we kmalloc'ed. */
+#define KPARAM_KMALLOCED       0x80000000
+
 #if 0
 #define DEBUGP printk
 #else
@@ -217,7 +220,19 @@ int param_set_charp(const char *val, struct kernel_param *kp)
                return -ENOSPC;
        }
 
-       *(char **)kp->arg = (char *)val;
+       if (kp->perm & KPARAM_KMALLOCED)
+               kfree(*(char **)kp->arg);
+
+       /* This is a hack.  We can't need to strdup in early boot, and we
+        * don't need to; this mangled commandline is preserved. */
+       if (slab_is_available()) {
+               kp->perm |= KPARAM_KMALLOCED;
+               *(char **)kp->arg = kstrdup(val, GFP_KERNEL);
+               if (!kp->arg)
+                       return -ENOMEM;
+       } else
+               *(const char **)kp->arg = val;
+
        return 0;
 }
 
@@ -571,6 +586,15 @@ void module_param_sysfs_remove(struct module *mod)
 }
 #endif
 
+void destroy_params(const struct kernel_param *params, unsigned num)
+{
+       unsigned int i;
+
+       for (i = 0; i < num; i++)
+               if (params[i].perm & KPARAM_KMALLOCED)
+                       kfree(*(char **)params[i].arg);
+}
+
 static void __init kernel_add_sysfs_param(const char *name,
                                          struct kernel_param *kparam,
                                          unsigned int name_skip)