[PATCH 1/3] driver core: class: Fix wild pointer dereference in API class_dev_iter_next()
From: Zijun Hu
Date: Mon Nov 04 2024 - 19:21:43 EST
From: Zijun Hu <quic_zijuhu@xxxxxxxxxxx>
class_dev_iter_init(struct class_dev_iter *iter, struct class *class, ...)
has return type void, but it does not initialize its output parameter @iter
when suffers class_to_subsys(@class) error, so caller can not detect the
error and call API class_dev_iter_next(@iter) which will dereference wild
pointers of @iter's members as shown by below typical usage:
// @iter's members are wild pointers
struct class_dev_iter iter;
// No change in @iter when the error happens.
class_dev_iter_init(&iter, ...);
// dereference these wild member pointers here.
while (dev = class_dev_iter_next(&iter)) { ... }.
Actually, all callers of the API have such usage pattern in kernel tree.
Fix by memset() @iter in API *_init() and error checking @iter in *_next().
Fixes: 7b884b7f24b4 ("driver core: class.c: convert to only use class_to_subsys")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Zijun Hu <quic_zijuhu@xxxxxxxxxxx>
---
Alternative fix solutions ever thought about:
1) Use BUG_ON(!sp) instead of error return in class_dev_iter_init().
2) Change class_dev_iter_init()'s type to int, lots of jobs to do.
---
drivers/base/class.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/base/class.c b/drivers/base/class.c
index cb5359235c70..b331dda002e3 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -323,8 +323,11 @@ void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class,
struct subsys_private *sp = class_to_subsys(class);
struct klist_node *start_knode = NULL;
- if (!sp)
+ memset(iter, 0, sizeof(*iter));
+ if (!sp) {
+ pr_crit("%s: the class was not registered yet\n", __func__);
return;
+ }
if (start)
start_knode = &start->p->knode_class;
@@ -351,6 +354,9 @@ struct device *class_dev_iter_next(struct class_dev_iter *iter)
struct klist_node *knode;
struct device *dev;
+ if (!iter->sp)
+ return NULL;
+
while (1) {
knode = klist_next(&iter->ki);
if (!knode)
--
2.34.1