params: fix potential memory leak in add_sysfs_param()
PATCH 1/1 params: fix potential memory leak in add_sysfs_param()
Do not leak memory when attrs is non NULL andkrealloc() fails. Without temporary variable,
reference to it is lost.
--- kernel/params.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-)
kernel/params.c +7 -9
@@ -594,7 +594,7 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, const char *name) {struct module_param_attrs *new; -struct attribute **attrs; +struct attribute **attrs, **new_attrs;int err, num;/* We don't bother calling this with invisible parameters. */
@@ -613,15 +613,12 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,sizeof (*mk->mp) +sizeof (mk->mp->attrs[0 ]) * (num+1 ), GFP_KERNEL);if (!new) { -kfree (attrs); err = -ENOMEM ; goto fail; } -/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ - attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); -if (!attrs) { + + new_attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); +if (!new_attrs) { err = -ENOMEM ; goto fail_free_new; }
@@ -629,9 +626,9 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,/* Sysfs wants everything zeroed. */ memset (new,0 ,sizeof (*new));memset (&new->attrs[num],0 ,sizeof (new->attrs[num])); -memset (&attrs[num],0 ,sizeof (attrs[num])); +memset (&new_attrs[num],0 ,sizeof (new_attrs[num])); new->grp.name ="parameters" ; - new->grp.attrs = attrs; + new->grp.attrs = new_attrs;/* Tack new one on the end. */ sysfs_attr_init (&new->attrs[num].mattr.attr);
@@ -653,6 +650,7 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, fail_free_new:kfree (new); fail: +kfree (attrs); mk->mp =NULL ;return err; }
Signature · 1 line
1.8.1.msysgit.1
Quoted 5 lines · Arjun Sreedharan
Do not leak memory when attrs is non NULL and
krealloc()fails. Without temporary variable,
reference to it is lost.Signed-off-by Arjun Sreedharan <[email protected]>
...
kernel/params.c static __modinit int add_sysfs_param(struct module_kobject *mk, · line 621
Quoted 9 lines
kernel/params.c +3
-5} -/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ - attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); -if (!attrs) { + + new_attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); +if (!new_attrs) {
I think that comment you deleted is pretty clear. Is it wrong?
Cheers,
Rusty.
Quoted 19 lines · Rusty Russell
Quoted 5 lines · Arjun Sreedharan
Do not leak memory when attrs is non NULL and
krealloc()fails. Without temporary variable,
reference to it is lost.Signed-off-by Arjun Sreedharan <[email protected]>...
kernel/params.c static __modinit int add_sysfs_param(struct module_kobject *mk, · line 621
Quoted 9 lines
}kernel/params.c +3
-5-/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ - attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); -if (!attrs) { + + new_attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); +if (!new_attrs) {
Quoted 4 lines
I think that comment you deleted is pretty clear. Is it wrong?
Cheers,
Rusty.
I believe it's wrong. I do not understand how this is safe from memory leak. If krealloc() fails, there is nothing in place to free memory held by @attrs
Quoted 20 lines · Arjun Sreedharan
Quoted 19 lines · Rusty Russell
Quoted 5 lines · Arjun Sreedharan
Do not leak memory when attrs is non NULL and
krealloc()fails. Without temporary variable,
reference to it is lost.Signed-off-by Arjun Sreedharan <[email protected]>...
Quoted 9 lines
}kernel/params.c +3
-5-/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ - attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); -if (!attrs) { + + new_attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); +if (!new_attrs) {
Quoted 7 lines
Quoted 4 lines
I think that comment you deleted is pretty clear. Is it wrong?
Cheers,
Rusty.I believe it's wrong. I do not understand how
this is safefrom memory leak. Ifkrealloc()fails, there is nothing in place to free memory held by @attrs
Above this:
if (!mk->mp) {
num = 0;
attrs = NULL;
} else {
num = mk->mp->num;
attrs = mk->mp->grp.attrs;
}So, attrs is just a temporary: either NULL (doesn't need freeing), or is the old mk->mp->grp.attrs ptr.
But David was the one who placed this comment; I'll let him figure out whether it's bogus or not :)
Cheers,
Rusty.
Quoted 10 lines · Rusty Russell
Above this:
if (!mk->mp) { num = 0; attrs = NULL; } else { num = mk->mp->num; attrs = mk->mp->grp.attrs; }
Quoted 2 lines
So, attrs is just a temporary: either NULL (doesn't need freeing), or is the old
mk->mp->grp.attrsptr.
Except that in the failure case we *free* the old mk->mp and never free mk->mp->grp.attrs so it *is* indeed lost.
A simpler version of Arjun's patch might look like this:
kernel/params.c +4 -2
@@ -613,7 +613,6 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,sizeof (*mk->mp) +sizeof (mk->mp->attrs[0 ]) * (num+1 ), GFP_KERNEL);if (!new) { -kfree (attrs); err = -ENOMEM ; goto fail; }
@@ -653,7 +652,10 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, fail_free_new:kfree (new); fail: - mk->mp =NULL ; +if (mk->mp) { +kfree (mk->mp->grp.attrs); + mk->mp =NULL ; + }return err; }
But as I suggested in my previous response, a *better* fix might look like this:
kernel/params.c +11 -16
@@ -595,7 +595,7 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, {struct module_param_attrs *new;struct attribute **attrs; -int err, num; +int num;/* We don't bother calling this with invisible parameters. */ BUG_ON (!kp->perm);
@@ -612,18 +612,19 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, new =krealloc (mk->mp,sizeof (*mk->mp) +sizeof (mk->mp->attrs[0 ]) * (num+1 ),GFP_KERNEL ); -if (!new) { -kfree (attrs); - err = -ENOMEM ; - goto fail; - } +if (!new) +return -ENOMEM ; +/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ +* In the failure case, the old 'attrs' is still in new->grp.attrs +* and will live on there. */ attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL );if (!attrs) { - err = -ENOMEM ; - goto fail_free_new; +/* This is in a larger kmalloc allocation than before but +* otherwise entirely unchanged. We've failed to add the +* new param but the existing ones are still there. */ + mk->mp = new; +return -ENOMEM ; }/* Sysfs wants everything zeroed. */
@@ -649,12 +650,6 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, mk->mp = new;return 0 ; - -fail_free_new: -kfree (new); -fail: - mk->mp =NULL ; -return err; }#ifdef CONFIG_MODULES
Signature · 2 lines
David Woodhouse Open Source Technology Centre [email protected] Intel Corporation
Quoted 19 lines · David
Except that in the failure case we *free* the old
mk->mpand never freemk->mp->grp.attrsso it *is* indeed lost.A simpler version of Arjun's patch might look like this:
kernel/params.c +0
-1@@ -613,7 +613,6 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,sizeof (*mk->mp) +sizeof (mk->mp->attrs[0 ]) * (num+1 ), GFP_KERNEL);if (!new) { -kfree (attrs); err = -ENOMEM ; goto fail; }
kernel/params.c static __modinit int add_sysfs_param(struct module_kobject *mk, · line 660
Quoted 10 lines
kernel/params.c +4
-1@@ -653,7 +652,10 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, fail_free_new:kfree (new); fail: - mk->mp =NULL ; +if (mk->mp) { +kfree (mk->mp->grp.attrs); + mk->mp =NULL ; + }return err; }
Of course this and Arjun's original *both* fail to fix the classic bug in the 'new = krealloc(mk->mp…)' bit, failing to free the old mk->mp if the realloc fails. I think my alternative fix that didn't kill the old params is probably the better way forward.
Signature · 1 line
dwmw2
Quoted 94 lines · David
Quoted 10 lines · Rusty Russell
Above this:
if (!mk->mp) { num = 0; attrs = NULL; } else { num = mk->mp->num; attrs = mk->mp->grp.attrs; }Quoted 2 lines
So, attrs is just a temporary: either NULL (doesn't need freeing), or is the old
mk->mp->grp.attrsptr.Except that in the failure case we *free* the old
mk->mpand never freemk->mp->grp.attrsso it *is* indeed lost.A simpler version of Arjun's patch might look like this:
kernel/params.c +4
-2@@ -613,7 +613,6 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,sizeof (*mk->mp) +sizeof (mk->mp->attrs[0 ]) * (num+1 ), GFP_KERNEL);if (!new) { -kfree (attrs); err = -ENOMEM ; goto fail; }@@ -653,7 +652,10 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, fail_free_new:kfree (new); fail: - mk->mp =NULL ; +if (mk->mp) { +kfree (mk->mp->grp.attrs); + mk->mp =NULL ; + }return err; }But as I suggested in my previous response, a *better* fix might look like this:
kernel/params.c +11
-10@@ -595,7 +595,7 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, {struct module_param_attrs *new;struct attribute **attrs; -int err, num; +int num;/* We don't bother calling this with invisible parameters. */ BUG_ON (!kp->perm);@@ -612,18 +612,19 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, new =krealloc (mk->mp,sizeof (*mk->mp) +sizeof (mk->mp->attrs[0 ]) * (num+1 ),GFP_KERNEL ); -if (!new) { -kfree (attrs); - err = -ENOMEM ; - goto fail; - } +if (!new) +return -ENOMEM ; +/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ +* In the failure case, the old 'attrs' is still in new->grp.attrs +* and will live on there. */ attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL );if (!attrs) { - err = -ENOMEM ; - goto fail_free_new; +/* This is in a larger kmalloc allocation than before but +* otherwise entirely unchanged. We've failed to add the +* new param but the existing ones are still there. */ + mk->mp = new; +return -ENOMEM ; }/* Sysfs wants everything zeroed. */ @@ -649,12 +650,6 @@ static __modinit int add_sysfs_param(struct module_kobject *mk, mk->mp = new;
kernel/params.c static __modinit int add_sysfs_param(struct module_kobject *mk, · line 655
Quoted 10 lines
kernel/params.c +0
-6return 0 ; - -fail_free_new: -kfree (new); -fail: - mk->mp =NULL ; -return err; }#ifdef CONFIG_MODULES
Ok, what's the harm in releasing {new,mk->mp}->grp.attrs and new/mk->mp on realloc failure ?
@David, @Rusty Am i right thinking they are not used after the function returns error ?
See module_param_sysfs_setup() : when the function in question returns error, free_module_param_attrs() is *not* called to release memory.
For cleaner code, we do *not* release memory in the said function as David suggests and then callfree_module_param_attrs() on returning err. What say?
Arjun
Quoted 4 lines
--
David Woodhouse Open Source Technology Centre
[email protected] Intel Corporation
Quoted 9 lines · Rusty Russell
Quoted 5 lines · Arjun Sreedharan
Do not leak memory when attrs is non NULL and
krealloc()fails. Without temporary variable,
reference to it is lost.Signed-off-by Arjun Sreedharan <[email protected]>...
Quoted 11 lines
kernel/params.c static __modinit int add_sysfs_param(struct module_kobject *mk, · line 621
Quoted 9 lines
kernel/params.c +3
-5} -/* Despite looking like the typical realloc() bug, this is safe. -* We *want* the old 'attrs' to be freed either way, and we'll store -* the new one in the success case. */ - attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); -if (!attrs) { + + new_attrs =krealloc (attrs,sizeof (new->grp.attrs[0 ])*(num+2 ),GFP_KERNEL ); +if (!new_attrs) {I think that comment you deleted is pretty clear. Is it wrong?
Yes, I think it's wrong.
I think I was under the impression that we shouldn't be freeing the old 'attrs' pointer on failure because it would live on in {new,mk->mp}->grp.attrs and still be used. (Which I concede isn't actually what the comment says, making it doubly wrong.)
I may have failed to notice that when this krealloc() fails, we actually *free* the 'new' pointer and set mk->mp to NULL. Without freeing the new->grp.attrs pointer. So it is indeed leaked. We *don't* need a temporary variable for it though; we can find it.
In fact, I wonder if it's better to change the 'goto fail_free_new' in the failure path here to instead do something like 'mk->mp = new; return -ENOMEM;'. That way, the existing params would still be present and we'd just have failed to add the *new* one. (And the leak wouldn't exist because the 'attrs' pointer would live on as I originally thought.)
Signature · 2 lines
David Woodhouse Open Source Technology Centre [email protected] Intel Corporation