aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2010-08-12 01:04:18 -0400
committerRusty Russell <rusty@rustcorp.com.au>2010-08-11 09:34:18 -0400
commita1054322afc8120ea5a50bc84e5beeda54571862 (patch)
treeee7d56b7b72ee51193067ede97d61d14535244fc /kernel
parente6df34a4429b77fdffb6e05adf263468a3dcda33 (diff)
param: use free hook for charp (fix leak of charp parameters)
Instead of using a "I kmalloced this" flag, we keep track of the kmalloced strings and use that list to check if we need to kfree (in practice, the list is very short). This means that kparams can be const again, and plugs a leak. This is important for drivers/usb/gadget/nokia.c which gets modprobe/rmmod'ed frequently on the N9000. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Reviewed-by: Takashi Iwai <tiwai@suse.de> Cc: Artem Bityutskiy <dedekind1@gmail.com> Tested-by: Phil Carmody <ext-phil.2.carmody@nokia.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/params.c52
1 files changed, 50 insertions, 2 deletions
diff --git a/kernel/params.c b/kernel/params.c
index 458a09b886c4..ef60db14fae0 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -31,6 +31,45 @@
31#define DEBUGP(fmt, a...) 31#define DEBUGP(fmt, a...)
32#endif 32#endif
33 33
34/* This just allows us to keep track of which parameters are kmalloced. */
35struct kmalloced_param {
36 struct list_head list;
37 char val[];
38};
39static DEFINE_MUTEX(param_lock);
40static LIST_HEAD(kmalloced_params);
41
42static void *kmalloc_parameter(unsigned int size)
43{
44 struct kmalloced_param *p;
45
46 p = kmalloc(sizeof(*p) + size, GFP_KERNEL);
47 if (!p)
48 return NULL;
49
50 mutex_lock(&param_lock);
51 list_add(&p->list, &kmalloced_params);
52 mutex_unlock(&param_lock);
53
54 return p->val;
55}
56
57/* Does nothing if parameter wasn't kmalloced above. */
58static void maybe_kfree_parameter(void *param)
59{
60 struct kmalloced_param *p;
61
62 mutex_lock(&param_lock);
63 list_for_each_entry(p, &kmalloced_params, list) {
64 if (p->val == param) {
65 list_del(&p->list);
66 kfree(p);
67 break;
68 }
69 }
70 mutex_unlock(&param_lock);
71}
72
34static inline char dash2underscore(char c) 73static inline char dash2underscore(char c)
35{ 74{
36 if (c == '-') 75 if (c == '-')
@@ -219,12 +258,15 @@ int param_set_charp(const char *val, const struct kernel_param *kp)
219 return -ENOSPC; 258 return -ENOSPC;
220 } 259 }
221 260
222 /* This is a hack. We can't need to strdup in early boot, and we 261 maybe_kfree_parameter(*(char **)kp->arg);
262
263 /* This is a hack. We can't kmalloc in early boot, and we
223 * don't need to; this mangled commandline is preserved. */ 264 * don't need to; this mangled commandline is preserved. */
224 if (slab_is_available()) { 265 if (slab_is_available()) {
225 *(char **)kp->arg = kstrdup(val, GFP_KERNEL); 266 *(char **)kp->arg = kmalloc_parameter(strlen(val)+1);
226 if (!*(char **)kp->arg) 267 if (!*(char **)kp->arg)
227 return -ENOMEM; 268 return -ENOMEM;
269 strcpy(*(char **)kp->arg, val);
228 } else 270 } else
229 *(const char **)kp->arg = val; 271 *(const char **)kp->arg = val;
230 272
@@ -238,9 +280,15 @@ int param_get_charp(char *buffer, const struct kernel_param *kp)
238} 280}
239EXPORT_SYMBOL(param_get_charp); 281EXPORT_SYMBOL(param_get_charp);
240 282
283static void param_free_charp(void *arg)
284{
285 maybe_kfree_parameter(*((char **)arg));
286}
287
241struct kernel_param_ops param_ops_charp = { 288struct kernel_param_ops param_ops_charp = {
242 .set = param_set_charp, 289 .set = param_set_charp,
243 .get = param_get_charp, 290 .get = param_get_charp,
291 .free = param_free_charp,
244}; 292};
245EXPORT_SYMBOL(param_ops_charp); 293EXPORT_SYMBOL(param_ops_charp);
246 294