Commit a15d49fd3094cff90e5410ca454a870e0a722fe1
Committed by
Greg Kroah-Hartman
1 parent
97ec448aea
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
driver core: check start node in klist_iter_init_node
klist_iter_init_node() takes a node as a start argument. However, this node might not be valid anymore. This patch updates the klist_iter_init_node() and dependent functions to return an error if so. All calling functions have been audited to check for a return code here. Signed-off-by: Hannes Reinecke <hare@suse.de> Cc: Greg Kroah-Hartmann <gregkh@linuxfoundation.org> Cc: Kay Sievers <kay@vrfy.org> Cc: Stable Kernel <stable@kernel.org> Cc: Linux Kernel <linux-kernel@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 6 changed files with 76 additions and 46 deletions Side-by-side Diff
drivers/base/bus.c
... | ... | @@ -296,11 +296,13 @@ |
296 | 296 | if (!bus) |
297 | 297 | return -EINVAL; |
298 | 298 | |
299 | - klist_iter_init_node(&bus->p->klist_devices, &i, | |
300 | - (start ? &start->p->knode_bus : NULL)); | |
301 | - while ((dev = next_device(&i)) && !error) | |
302 | - error = fn(dev, data); | |
303 | - klist_iter_exit(&i); | |
299 | + error = klist_iter_init_node(&bus->p->klist_devices, &i, | |
300 | + (start ? &start->p->knode_bus : NULL)); | |
301 | + if (!error) { | |
302 | + while ((dev = next_device(&i)) && !error) | |
303 | + error = fn(dev, data); | |
304 | + klist_iter_exit(&i); | |
305 | + } | |
304 | 306 | return error; |
305 | 307 | } |
306 | 308 | EXPORT_SYMBOL_GPL(bus_for_each_dev); |
... | ... | @@ -330,8 +332,10 @@ |
330 | 332 | if (!bus) |
331 | 333 | return NULL; |
332 | 334 | |
333 | - klist_iter_init_node(&bus->p->klist_devices, &i, | |
334 | - (start ? &start->p->knode_bus : NULL)); | |
335 | + if (klist_iter_init_node(&bus->p->klist_devices, &i, | |
336 | + (start ? &start->p->knode_bus : NULL)) < 0) | |
337 | + return NULL; | |
338 | + | |
335 | 339 | while ((dev = next_device(&i))) |
336 | 340 | if (match(dev, data) && get_device(dev)) |
337 | 341 | break; |
... | ... | @@ -384,7 +388,9 @@ |
384 | 388 | return NULL; |
385 | 389 | |
386 | 390 | if (hint) { |
387 | - klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus); | |
391 | + if (klist_iter_init_node(&subsys->p->klist_devices, &i, | |
392 | + &hint->p->knode_bus) < 0) | |
393 | + return NULL; | |
388 | 394 | dev = next_device(&i); |
389 | 395 | if (dev && dev->id == id && get_device(dev)) { |
390 | 396 | klist_iter_exit(&i); |
... | ... | @@ -446,11 +452,13 @@ |
446 | 452 | if (!bus) |
447 | 453 | return -EINVAL; |
448 | 454 | |
449 | - klist_iter_init_node(&bus->p->klist_drivers, &i, | |
450 | - start ? &start->p->knode_bus : NULL); | |
451 | - while ((drv = next_driver(&i)) && !error) | |
452 | - error = fn(drv, data); | |
453 | - klist_iter_exit(&i); | |
455 | + error = klist_iter_init_node(&bus->p->klist_drivers, &i, | |
456 | + start ? &start->p->knode_bus : NULL); | |
457 | + if (!error) { | |
458 | + while ((drv = next_driver(&i)) && !error) | |
459 | + error = fn(drv, data); | |
460 | + klist_iter_exit(&i); | |
461 | + } | |
454 | 462 | return error; |
455 | 463 | } |
456 | 464 | EXPORT_SYMBOL_GPL(bus_for_each_drv); |
457 | 465 | |
458 | 466 | |
... | ... | @@ -1111,15 +1119,19 @@ |
1111 | 1119 | * otherwise if it is NULL, the iteration starts at the beginning of |
1112 | 1120 | * the list. |
1113 | 1121 | */ |
1114 | -void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, | |
1115 | - struct device *start, const struct device_type *type) | |
1122 | +int subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, | |
1123 | + struct device *start, const struct device_type *type) | |
1116 | 1124 | { |
1117 | 1125 | struct klist_node *start_knode = NULL; |
1126 | + int error; | |
1118 | 1127 | |
1119 | 1128 | if (start) |
1120 | 1129 | start_knode = &start->p->knode_bus; |
1121 | - klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode); | |
1122 | - iter->type = type; | |
1130 | + error = klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, | |
1131 | + start_knode); | |
1132 | + if (!error) | |
1133 | + iter->type = type; | |
1134 | + return error; | |
1123 | 1135 | } |
1124 | 1136 | EXPORT_SYMBOL_GPL(subsys_dev_iter_init); |
1125 | 1137 |
drivers/base/class.c
... | ... | @@ -301,15 +301,20 @@ |
301 | 301 | * otherwise if it is NULL, the iteration starts at the beginning of |
302 | 302 | * the list. |
303 | 303 | */ |
304 | -void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, | |
305 | - struct device *start, const struct device_type *type) | |
304 | +int class_dev_iter_init(struct class_dev_iter *iter, struct class *class, | |
305 | + struct device *start, const struct device_type *type) | |
306 | 306 | { |
307 | 307 | struct klist_node *start_knode = NULL; |
308 | + int error; | |
308 | 309 | |
309 | 310 | if (start) |
310 | 311 | start_knode = &start->knode_class; |
311 | - klist_iter_init_node(&class->p->klist_devices, &iter->ki, start_knode); | |
312 | - iter->type = type; | |
312 | + error = klist_iter_init_node(&class->p->klist_devices, &iter->ki, | |
313 | + start_knode); | |
314 | + if (!error) | |
315 | + iter->type = type; | |
316 | + | |
317 | + return error; | |
313 | 318 | } |
314 | 319 | EXPORT_SYMBOL_GPL(class_dev_iter_init); |
315 | 320 | |
316 | 321 | |
... | ... | @@ -387,14 +392,15 @@ |
387 | 392 | return -EINVAL; |
388 | 393 | } |
389 | 394 | |
390 | - class_dev_iter_init(&iter, class, start, NULL); | |
391 | - while ((dev = class_dev_iter_next(&iter))) { | |
392 | - error = fn(dev, data); | |
393 | - if (error) | |
394 | - break; | |
395 | + error = class_dev_iter_init(&iter, class, start, NULL); | |
396 | + if (!error) { | |
397 | + while ((dev = class_dev_iter_next(&iter))) { | |
398 | + error = fn(dev, data); | |
399 | + if (error) | |
400 | + break; | |
401 | + } | |
402 | + class_dev_iter_exit(&iter); | |
395 | 403 | } |
396 | - class_dev_iter_exit(&iter); | |
397 | - | |
398 | 404 | return error; |
399 | 405 | } |
400 | 406 | EXPORT_SYMBOL_GPL(class_for_each_device); |
... | ... | @@ -434,7 +440,9 @@ |
434 | 440 | return NULL; |
435 | 441 | } |
436 | 442 | |
437 | - class_dev_iter_init(&iter, class, start, NULL); | |
443 | + if (class_dev_iter_init(&iter, class, start, NULL) < 0) | |
444 | + return NULL; | |
445 | + | |
438 | 446 | while ((dev = class_dev_iter_next(&iter))) { |
439 | 447 | if (match(dev, data)) { |
440 | 448 | get_device(dev); |
drivers/base/driver.c
... | ... | @@ -49,11 +49,13 @@ |
49 | 49 | if (!drv) |
50 | 50 | return -EINVAL; |
51 | 51 | |
52 | - klist_iter_init_node(&drv->p->klist_devices, &i, | |
53 | - start ? &start->p->knode_driver : NULL); | |
54 | - while ((dev = next_device(&i)) && !error) | |
55 | - error = fn(dev, data); | |
56 | - klist_iter_exit(&i); | |
52 | + error = klist_iter_init_node(&drv->p->klist_devices, &i, | |
53 | + start ? &start->p->knode_driver : NULL); | |
54 | + if (!error) { | |
55 | + while ((dev = next_device(&i)) && !error) | |
56 | + error = fn(dev, data); | |
57 | + klist_iter_exit(&i); | |
58 | + } | |
57 | 59 | return error; |
58 | 60 | } |
59 | 61 | EXPORT_SYMBOL_GPL(driver_for_each_device); |
... | ... | @@ -83,8 +85,10 @@ |
83 | 85 | if (!drv) |
84 | 86 | return NULL; |
85 | 87 | |
86 | - klist_iter_init_node(&drv->p->klist_devices, &i, | |
87 | - (start ? &start->p->knode_driver : NULL)); | |
88 | + if (klist_iter_init_node(&drv->p->klist_devices, &i, | |
89 | + (start ? &start->p->knode_driver : NULL)) < 0) | |
90 | + return NULL; | |
91 | + | |
88 | 92 | while ((dev = next_device(&i))) |
89 | 93 | if (match(dev, data) && get_device(dev)) |
90 | 94 | break; |
include/linux/device.h
... | ... | @@ -128,7 +128,7 @@ |
128 | 128 | struct klist_iter ki; |
129 | 129 | const struct device_type *type; |
130 | 130 | }; |
131 | -void subsys_dev_iter_init(struct subsys_dev_iter *iter, | |
131 | +int subsys_dev_iter_init(struct subsys_dev_iter *iter, | |
132 | 132 | struct bus_type *subsys, |
133 | 133 | struct device *start, |
134 | 134 | const struct device_type *type); |
... | ... | @@ -380,10 +380,10 @@ |
380 | 380 | void class_compat_remove_link(struct class_compat *cls, struct device *dev, |
381 | 381 | struct device *device_link); |
382 | 382 | |
383 | -extern void class_dev_iter_init(struct class_dev_iter *iter, | |
384 | - struct class *class, | |
385 | - struct device *start, | |
386 | - const struct device_type *type); | |
383 | +extern int class_dev_iter_init(struct class_dev_iter *iter, | |
384 | + struct class *class, | |
385 | + struct device *start, | |
386 | + const struct device_type *type); | |
387 | 387 | extern struct device *class_dev_iter_next(struct class_dev_iter *iter); |
388 | 388 | extern void class_dev_iter_exit(struct class_dev_iter *iter); |
389 | 389 |
include/linux/klist.h
... | ... | @@ -60,7 +60,7 @@ |
60 | 60 | |
61 | 61 | |
62 | 62 | extern void klist_iter_init(struct klist *k, struct klist_iter *i); |
63 | -extern void klist_iter_init_node(struct klist *k, struct klist_iter *i, | |
63 | +extern int klist_iter_init_node(struct klist *k, struct klist_iter *i, | |
64 | 64 | struct klist_node *n); |
65 | 65 | extern void klist_iter_exit(struct klist_iter *i); |
66 | 66 | extern struct klist_node *klist_next(struct klist_iter *i); |
lib/klist.c
... | ... | @@ -278,13 +278,19 @@ |
278 | 278 | * Similar to klist_iter_init(), but starts the action off with @n, |
279 | 279 | * instead of with the list head. |
280 | 280 | */ |
281 | -void klist_iter_init_node(struct klist *k, struct klist_iter *i, | |
282 | - struct klist_node *n) | |
281 | +int klist_iter_init_node(struct klist *k, struct klist_iter *i, | |
282 | + struct klist_node *n) | |
283 | 283 | { |
284 | + if (n) { | |
285 | + kref_get(&n->n_ref); | |
286 | + if (!n->n_klist) { | |
287 | + kref_put(&n->n_ref); | |
288 | + return -ENODEV; | |
289 | + } | |
290 | + } | |
284 | 291 | i->i_klist = k; |
285 | 292 | i->i_cur = n; |
286 | - if (n) | |
287 | - kref_get(&n->n_ref); | |
293 | + return 0; | |
288 | 294 | } |
289 | 295 | EXPORT_SYMBOL_GPL(klist_iter_init_node); |
290 | 296 |