summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2010-08-11 23:04:18 -0600
committerRusty Russell <rusty@rustcorp.com.au>2010-08-11 23:04:18 +0930
commita1054322afc8120ea5a50bc84e5beeda54571862 (patch)
treeee7d56b7b72ee51193067ede97d61d14535244fc
parente6df34a4429b77fdffb6e05adf263468a3dcda33 (diff)
downloadlwn-a1054322afc8120ea5a50bc84e5beeda54571862.tar.gz
lwn-a1054322afc8120ea5a50bc84e5beeda54571862.zip
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>
-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 @@
#define DEBUGP(fmt, a...)
#endif
+/* This just allows us to keep track of which parameters are kmalloced. */
+struct kmalloced_param {
+ struct list_head list;
+ char val[];
+};
+static DEFINE_MUTEX(param_lock);
+static LIST_HEAD(kmalloced_params);
+
+static void *kmalloc_parameter(unsigned int size)
+{
+ struct kmalloced_param *p;
+
+ p = kmalloc(sizeof(*p) + size, GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ mutex_lock(&param_lock);
+ list_add(&p->list, &kmalloced_params);
+ mutex_unlock(&param_lock);
+
+ return p->val;
+}
+
+/* Does nothing if parameter wasn't kmalloced above. */
+static void maybe_kfree_parameter(void *param)
+{
+ struct kmalloced_param *p;
+
+ mutex_lock(&param_lock);
+ list_for_each_entry(p, &kmalloced_params, list) {
+ if (p->val == param) {
+ list_del(&p->list);
+ kfree(p);
+ break;
+ }
+ }
+ mutex_unlock(&param_lock);
+}
+
static inline char dash2underscore(char c)
{
if (c == '-')
@@ -219,12 +258,15 @@ int param_set_charp(const char *val, const struct kernel_param *kp)
return -ENOSPC;
}
- /* This is a hack. We can't need to strdup in early boot, and we
+ maybe_kfree_parameter(*(char **)kp->arg);
+
+ /* This is a hack. We can't kmalloc in early boot, and we
* don't need to; this mangled commandline is preserved. */
if (slab_is_available()) {
- *(char **)kp->arg = kstrdup(val, GFP_KERNEL);
+ *(char **)kp->arg = kmalloc_parameter(strlen(val)+1);
if (!*(char **)kp->arg)
return -ENOMEM;
+ strcpy(*(char **)kp->arg, val);
} else
*(const char **)kp->arg = val;
@@ -238,9 +280,15 @@ int param_get_charp(char *buffer, const struct kernel_param *kp)
}
EXPORT_SYMBOL(param_get_charp);
+static void param_free_charp(void *arg)
+{
+ maybe_kfree_parameter(*((char **)arg));
+}
+
struct kernel_param_ops param_ops_charp = {
.set = param_set_charp,
.get = param_get_charp,
+ .free = param_free_charp,
};
EXPORT_SYMBOL(param_ops_charp);