Skip to content

Commit e180a6b

Browse files
committed
param: fix charp parameters set via sysfs
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>
1 parent 15f7176 commit e180a6b

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

include/linux/module.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ struct module
248248
const unsigned long *crcs;
249249
unsigned int num_syms;
250250

251+
/* Kernel parameters. */
252+
struct kernel_param *kp;
253+
unsigned int num_kp;
254+
251255
/* GPL-only exported symbols. */
252256
unsigned int num_gpl_syms;
253257
const struct kernel_symbol *gpl_syms;

include/linux/moduleparam.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ extern int parse_args(const char *name,
138138
unsigned num,
139139
int (*unknown)(char *param, char *val));
140140

141+
/* Called by module remove. */
142+
#ifdef CONFIG_SYSFS
143+
extern void destroy_params(const struct kernel_param *params, unsigned num);
144+
#else
145+
static inline void destroy_params(const struct kernel_param *params,
146+
unsigned num)
147+
{
148+
}
149+
#endif /* !CONFIG_SYSFS */
150+
141151
/* All the helper functions */
142152
/* The macros to do compile-time type checking stolen from Jakub
143153
Jelinek, who IIRC came up with this idea for the 2.4 module init code. */

kernel/module.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,9 @@ static void free_module(struct module *mod)
14911491
/* Module unload stuff */
14921492
module_unload_free(mod);
14931493

1494+
/* Free any allocated parameters. */
1495+
destroy_params(mod->kp, mod->num_kp);
1496+
14941497
/* release any pointers to mcount in this module */
14951498
ftrace_release(mod->module_core, mod->core_size);
14961499

@@ -1898,8 +1901,7 @@ static noinline struct module *load_module(void __user *umod,
18981901
unsigned int symindex = 0;
18991902
unsigned int strindex = 0;
19001903
unsigned int modindex, versindex, infoindex, pcpuindex;
1901-
unsigned int num_kp, num_mcount;
1902-
struct kernel_param *kp;
1904+
unsigned int num_mcount;
19031905
struct module *mod;
19041906
long err = 0;
19051907
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
@@ -2144,8 +2146,8 @@ static noinline struct module *load_module(void __user *umod,
21442146

21452147
/* Now we've got everything in the final locations, we can
21462148
* find optional sections. */
2147-
kp = section_objs(hdr, sechdrs, secstrings, "__param", sizeof(*kp),
2148-
&num_kp);
2149+
mod->kp = section_objs(hdr, sechdrs, secstrings, "__param",
2150+
sizeof(*mod->kp), &mod->num_kp);
21492151
mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab",
21502152
sizeof(*mod->syms), &mod->num_syms);
21512153
mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab");
@@ -2291,11 +2293,11 @@ static noinline struct module *load_module(void __user *umod,
22912293
*/
22922294
list_add_rcu(&mod->list, &modules);
22932295

2294-
err = parse_args(mod->name, mod->args, kp, num_kp, NULL);
2296+
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
22952297
if (err < 0)
22962298
goto unlink;
22972299

2298-
err = mod_sysfs_setup(mod, kp, num_kp);
2300+
err = mod_sysfs_setup(mod, mod->kp, mod->num_kp);
22992301
if (err < 0)
23002302
goto unlink;
23012303
add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);

kernel/params.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#include <linux/err.h>
2525
#include <linux/slab.h>
2626

27+
/* We abuse the high bits of "perm" to record whether we kmalloc'ed. */
28+
#define KPARAM_KMALLOCED 0x80000000
29+
2730
#if 0
2831
#define DEBUGP printk
2932
#else
@@ -217,7 +220,19 @@ int param_set_charp(const char *val, struct kernel_param *kp)
217220
return -ENOSPC;
218221
}
219222

220-
*(char **)kp->arg = (char *)val;
223+
if (kp->perm & KPARAM_KMALLOCED)
224+
kfree(*(char **)kp->arg);
225+
226+
/* This is a hack. We can't need to strdup in early boot, and we
227+
* don't need to; this mangled commandline is preserved. */
228+
if (slab_is_available()) {
229+
kp->perm |= KPARAM_KMALLOCED;
230+
*(char **)kp->arg = kstrdup(val, GFP_KERNEL);
231+
if (!kp->arg)
232+
return -ENOMEM;
233+
} else
234+
*(const char **)kp->arg = val;
235+
221236
return 0;
222237
}
223238

@@ -571,6 +586,15 @@ void module_param_sysfs_remove(struct module *mod)
571586
}
572587
#endif
573588

589+
void destroy_params(const struct kernel_param *params, unsigned num)
590+
{
591+
unsigned int i;
592+
593+
for (i = 0; i < num; i++)
594+
if (params[i].perm & KPARAM_KMALLOCED)
595+
kfree(*(char **)params[i].arg);
596+
}
597+
574598
static void __init kernel_add_sysfs_param(const char *name,
575599
struct kernel_param *kparam,
576600
unsigned int name_skip)

0 commit comments

Comments
 (0)