* [PATCH] param: fix charp parameters set via sysfs
@ 2009-02-16 23:40 Rusty Russell
2009-03-06 17:58 ` Sitsofe Wheeler
0 siblings, 1 reply; 3+ messages in thread
From: Rusty Russell @ 2009-02-16 23:40 UTC (permalink / raw)
To: linux-kernel, Linus Torvalds
Cc: Sitsofe Wheeler, Frederic Weisbecker, Greg KH, stable,
Sitsofe Wheeler, Frederic Weisbecker, Frederic Weisbecker
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 freed mem.
So we kstrdup instead: this is fine, but it means 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.
Reported-by: Sitsofe Wheeler <sitsofe@yahoo.com>
Diagnosed-by: Frederic Weisbecker <fweisbec@gmail.com>
Tested-by: Frederic Weisbecker <fweisbec@gmail.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
diff --git a/include/linux/module.h b/include/linux/module.h
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -247,6 +247,10 @@ struct module
const struct kernel_symbol *syms;
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;
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -138,6 +138,9 @@ extern int parse_args(const char *name,
unsigned num,
int (*unknown)(char *param, char *val));
+/* Called by module remove. */
+extern void destroy_params(const struct kernel_param *params, unsigned num);
+
/* 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. */
diff --git a/kernel/module.c b/kernel/module.c
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1457,6 +1457,9 @@ static void free_module(struct module *m
/* 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);
@@ -1870,8 +1873,7 @@ static noinline struct module *load_modu
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 */
@@ -2116,8 +2118,8 @@ static noinline struct module *load_modu
/* 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");
@@ -2262,11 +2264,11 @@ static noinline struct module *load_modu
*/
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);
diff --git a/kernel/params.c b/kernel/params.c
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -23,6 +23,9 @@
#include <linux/device.h>
#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
@@ -217,7 +220,13 @@ int param_set_charp(const char *val, str
return -ENOSPC;
}
- *(char **)kp->arg = (char *)val;
+ if (kp->perm & KPARAM_KMALLOCED)
+ kfree(*(char **)kp->arg);
+
+ kp->perm |= KPARAM_KMALLOCED;
+ *(char **)kp->arg = kstrdup(val, GFP_KERNEL);
+ if (!kp->arg)
+ return -ENOMEM;
return 0;
}
@@ -571,6 +580,15 @@ void module_param_sysfs_remove(struct mo
}
#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)
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] param: fix charp parameters set via sysfs
2009-02-16 23:40 [PATCH] param: fix charp parameters set via sysfs Rusty Russell
@ 2009-03-06 17:58 ` Sitsofe Wheeler
2009-03-07 2:28 ` Rusty Russell
0 siblings, 1 reply; 3+ messages in thread
From: Sitsofe Wheeler @ 2009-03-06 17:58 UTC (permalink / raw)
To: Rusty Russell
Cc: linux-kernel, Linus Torvalds, Frederic Weisbecker, Greg KH,
stable
On Tue, Feb 17, 2009 at 10:10:34AM +1030, Rusty Russell wrote:
> Impact: fix crash on reading from /sys/module/.../ieee80211_default_rc_algo
Is this too late to be queued for 2.6.30 (I guess it missed the current
window as it doesn't appear to be in 2.6.29-rc7)?
--
Sitsofe | http://sucs.org/~sits/
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] param: fix charp parameters set via sysfs
2009-03-06 17:58 ` Sitsofe Wheeler
@ 2009-03-07 2:28 ` Rusty Russell
0 siblings, 0 replies; 3+ messages in thread
From: Rusty Russell @ 2009-03-07 2:28 UTC (permalink / raw)
To: Sitsofe Wheeler
Cc: linux-kernel, Linus Torvalds, Frederic Weisbecker, Greg KH,
stable
On Saturday 07 March 2009 04:28:55 Sitsofe Wheeler wrote:
> On Tue, Feb 17, 2009 at 10:10:34AM +1030, Rusty Russell wrote:
> > Impact: fix crash on reading from /sys/module/.../ieee80211_default_rc_algo
>
> Is this too late to be queued for 2.6.30 (I guess it missed the current
> window as it doesn't appear to be in 2.6.29-rc7)?
Linus didn't take it. Since it predates 2.6.29 and not a one-line fix, I
guessed that meant he doesn't want it in 2.6.30.
Just in case, here it is again:
Subject: 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>
---
kernel/params.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/include/linux/module.h b/include/linux/module.h
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -247,6 +247,10 @@ struct module
const struct kernel_symbol *syms;
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;
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -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. */
diff --git a/kernel/module.c b/kernel/module.c
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1457,6 +1457,9 @@ static void free_module(struct module *m
/* 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);
@@ -1870,8 +1873,7 @@ static noinline struct module *load_modu
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 */
@@ -2116,8 +2118,8 @@ static noinline struct module *load_modu
/* 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");
@@ -2262,11 +2264,11 @@ static noinline struct module *load_modu
*/
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);
diff --git a/kernel/params.c b/kernel/params.c
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -23,6 +23,9 @@
#include <linux/device.h>
#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
@@ -217,7 +220,19 @@ int param_set_charp(const char *val, str
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 mo
}
#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)
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2009-03-07 2:28 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-16 23:40 [PATCH] param: fix charp parameters set via sysfs Rusty Russell
2009-03-06 17:58 ` Sitsofe Wheeler
2009-03-07 2:28 ` Rusty Russell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox