[PATCH 05/11] class: fix possible memory leak in __class_register()

Yang Yingliang
2022-10-21T02:24:10Z

Inject fault while loading module (e.g. pktcdvd.ko), kset_register() may
fail in __class_register(), if it fails, but the refcount of kobject is
not decreased to 0, the name allocated in kobject_set_name() is leaked.
To fix this by calling kfree_const().

unreferenced object 0xffff888102fa8190 (size 8):
comm "modprobe", pid 502, jiffies 4294906074 (age 49.296s)
hex dump (first 8 bytes):
70 6b 74 63 64 76 64 00 pktcdvd.
backtrace:
[<00000000e7c7703d>] __kmalloc_track_caller+0x1ae/0x320
[<000000005e4d70bc>] kstrdup+0x3a/0x70
[<00000000c2e5e85a>] kstrdup_const+0x68/0x80
[<000000000049a8c7>] kvasprintf_const+0x10b/0x190
[<0000000029123163>] kobject_set_name_vargs+0x56/0x150
[<00000000747219c9>] kobject_set_name+0xab/0xe0
[<0000000005f1ea4e>] __class_register+0x15c/0x49a

If class_add_groups() fails, it need delete kobject and free its name,
besides, subsys_private also need be freed.

unreferenced object 0xffff888037274000 (size 1024):
comm "modprobe", pid 502, jiffies 4294906074 (age 49.296s)
hex dump (first 32 bytes):
00 40 27 37 80 88 ff ff 00 40 27 37 80 88 ff ff .@'7.....@'7....
00 00 00 00 ad 4e ad de ff ff ff ff 00 00 00 00 .....N..........
backtrace:
[<00000000151f9600>] kmem_cache_alloc_trace+0x17c/0x2f0
[<00000000ecf3dd95>] __class_register+0x86/0x49a

It can not call kset_put() or kset_unregister() in error path, because
the 'cls' will be freed in callback function class_release() and it also
freed in error path, it will cause double free.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
---
drivers/base/class.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/base/class.c b/drivers/base/class.c
index 64f7b9a0970f..87de0a04ee9b 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -187,11 +187,17 @@ int __class_register(struct class *cls, struct lock_class_key *key)

error = kset_register(&cp->subsys);
if (error) {
+ kfree_const(cp->subsys.kobj.name);
kfree(cp);
return error;
}
error = class_add_groups(class_get(cls), cls->class_groups);
class_put(cls);
+ if (error) {
+ kobject_del(&cp->subsys.kobj);
+ kfree_const(cp->subsys.kobj.name);
+ kfree(cp);
+ }
return error;
}
EXPORT_SYMBOL_GPL(__class_register);
--
2.25.1