leds: fix broken devres usage
cover leds: fix broken devres usage
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Included is also a clean up removing some pointless casts when registering class devices.
All but the lm3533 one have only been compile tested.
Johan
Johan Hovold (6):
leds: 88pm860x: fix use-after-free on unbind
leds: da903x: fix use-after-free on unbind
leds: lm3533: fix use-after-free on unbind
leds: lm36274: fix use-after-free on unbind
leds: wm831x-status: fix use-after-free on unbind
leds: drop redundant struct-device pointer casts
drivers/leds/leds-88pm860x.c | 14 +++++++++++++- drivers/leds/leds-da903x.c | 14 +++++++++++++- drivers/leds/leds-lm3533.c | 12 +++++++++--- drivers/leds/leds-lm355x.c | 9 +++------ drivers/leds/leds-lm36274.c | 15 ++++++++++++--- drivers/leds/leds-lm3642.c | 9 +++------ drivers/leds/leds-wm831x-status.c | 14 +++++++++++++- 7 files changed, 66 insertions(+), 21 deletions(-)
Signature · 1 line
2.26.2
PATCH 1/6 leds: 88pm860x: fix use-after-free on unbind
Several MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered.
--- drivers/leds/leds-88pm860x.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/leds/leds-88pm860x.c +13 -1
@@ -203,21 +203,33 @@ static int pm860x_led_probe(struct platform_device *pdev) data->cdev.brightness_set_blocking = pm860x_led_set;mutex_init (&data->lock); - ret =devm_led_classdev_register (chip->dev, &data->cdev); + ret =led_classdev_register (chip->dev, &data->cdev);if (ret <0 ) {dev_err (&pdev->dev,"Failed to register LED: %d\n" , ret);return ret; }pm860x_led_set (&data->cdev,0 ); + +platform_set_drvdata (pdev, data); +return 0 ; } +static int pm860x_led_remove (struct platform_device *pdev) +{ +struct pm860x_led *data =platform_get_drvdata (pdev); + +led_classdev_unregister (&data->cdev); + +return 0 ; +}static struct platform_driver pm860x_led_driver = { .driver = { .name ="88pm860x-led" , }, .probe = pm860x_led_probe, + .remove = pm860x_led_remove, };module_platform_driver (pm860x_led_driver);
Signature · 1 line
2.26.2
PATCH 2/6 leds: da903x: fix use-after-free on unbind
In reply to Johan HovoldSeveral MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered.
--- drivers/leds/leds-da903x.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/leds/leds-da903x.c +13 -1
@@ -110,12 +110,23 @@ static int da903x_led_probe(struct platform_device *pdev) led->flags = pdata->flags; led->master = pdev->dev.parent; - ret =devm_led_classdev_register (led->master, &led->cdev); + ret =led_classdev_register (led->master, &led->cdev);if (ret) {dev_err (&pdev->dev,"failed to register LED %d\n" , id);return ret; } +platform_set_drvdata (pdev, led); + +return 0 ; +} + +static int da903x_led_remove (struct platform_device *pdev) +{ +struct da903x_led *led =platform_get_drvdata (pdev); + +led_classdev_unregister (&led->cdev); +return 0 ; }
@@ -124,6 +135,7 @@ static struct platform_driver da903x_led_driver = { .name = "da903x-led", }, .probe = da903x_led_probe, + .remove = da903x_led_remove, };module_platform_driver (da903x_led_driver);
Signature · 1 line
2.26.2
PATCH 3/6 leds: lm3533: fix use-after-free on unbind
In reply to Johan HovoldSeveral MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered.
--- drivers/leds/leds-lm3533.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/leds/leds-lm3533.c +9 -3
@@ -694,7 +694,7 @@ static int lm3533_led_probe(struct platform_device *pdev)platform_set_drvdata (pdev, led); - ret =devm_led_classdev_register (pdev->dev.parent, &led->cdev); + ret =led_classdev_register (pdev->dev.parent, &led->cdev);if (ret) {dev_err (&pdev->dev,"failed to register LED %d\n" , pdev->id);return ret;
@@ -704,13 +704,18 @@ static int lm3533_led_probe(struct platform_device *pdev) ret =lm3533_led_setup (led, pdata);if (ret) -return ret; + goto err_deregister; ret =lm3533_ctrlbank_enable (&led->cb);if (ret) -return ret; + goto err_deregister;return 0 ; + +err_deregister: +led_classdev_unregister (&led->cdev); + +return ret; }static int lm3533_led_remove (struct platform_device *pdev)
@@ -720,6 +725,7 @@ static int lm3533_led_remove(struct platform_device *pdev)dev_dbg (&pdev->dev,"%s\n" , __func__);lm3533_ctrlbank_disable (&led->cb); +led_classdev_unregister (&led->cdev);return 0 ; }
Signature · 1 line
2.26.2
PATCH 4/6 leds: lm36274: fix use-after-free on unbind
In reply to Johan HovoldSeveral MFD child drivers register their class devices directly under the parent device. This means you cannot use devres so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered.
--- drivers/leds/leds-lm36274.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
drivers/leds/leds-lm36274.c +12 -3
@@ -133,7 +133,7 @@ static int lm36274_probe(struct platform_device *pdev) lm36274_data->pdev = pdev; lm36274_data->dev = lmu->dev; lm36274_data->regmap = lmu->regmap; -dev_set_drvdata (&pdev->dev, lm36274_data); +platform_set_drvdata (pdev, lm36274_data); ret =lm36274_parse_dt (lm36274_data);if (ret) {
@@ -147,8 +147,16 @@ static int lm36274_probe(struct platform_device *pdev)return ret; } -return devm_led_classdev_register (lm36274_data->dev, - &lm36274_data->led_dev); +return led_classdev_register (lm36274_data->dev, &lm36274_data->led_dev); +} + +static int lm36274_remove (struct platform_device *pdev) +{ +struct lm36274 *lm36274_data =platform_get_drvdata (pdev); + +led_classdev_unregister (&lm36274_data->led_dev); + +return 0 ; }static const struct of_device_id of_lm36274_leds_match[] = {
@@ -159,6 +167,7 @@ MODULE_DEVICE_TABLE(of, of_lm36274_leds_match);static struct platform_driver lm36274_driver = { .probe = lm36274_probe, + .remove = lm36274_remove, .driver = { .name ="lm36274-leds" , },
Signature · 1 line
2.26.2
PATCH 5/6 leds: wm831x-status: fix use-after-free on unbind
In reply to Johan HovoldSeveral MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered.
--- drivers/leds/leds-wm831x-status.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/leds/leds-wm831x-status.c +13 -1
@@ -269,12 +269,23 @@ static int wm831x_status_probe(struct platform_device *pdev) drvdata->cdev.blink_set = wm831x_status_blink_set; drvdata->cdev.groups = wm831x_status_groups; - ret =devm_led_classdev_register (wm831x->dev, &drvdata->cdev); + ret =led_classdev_register (wm831x->dev, &drvdata->cdev);if (ret <0 ) {dev_err (&pdev->dev,"Failed to register LED: %d\n" , ret);return ret; } +platform_set_drvdata (pdev, drvdata); + +return 0 ; +} + +static int wm831x_status_remove (struct platform_device *pdev) +{ +struct wm831x_status *drvdata =platform_get_drvdata (pdev); + +led_classdev_unregister (&drvdata->cdev); +return 0 ; }
@@ -283,6 +294,7 @@ static struct platform_driver wm831x_status_driver = { .name = "wm831x-status", }, .probe = wm831x_status_probe, + .remove = wm831x_status_remove, };module_platform_driver (wm831x_status_driver);
Signature · 1 line
2.26.2
PATCH 6/6 leds: drop redundant struct-device pointer casts
In reply to Johan HovoldDrop the pointless and needlessly confusing casts of struct-device pointers.
--- drivers/leds/leds-lm355x.c | 9 +++------ drivers/leds/leds-lm3642.c | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-)
drivers/leds/leds-lm355x.c +3 -6
@@ -453,8 +453,7 @@ static int lm355x_probe(struct i2c_client *client, chip->cdev_flash.max_brightness =16 ; chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set; chip->cdev_flash.default_trigger ="flash" ; - err =led_classdev_register ((struct device *) - &client->dev, &chip->cdev_flash); + err =led_classdev_register (&client->dev, &chip->cdev_flash);if (err <0 ) goto err_out;/* torch */
@@ -462,8 +461,7 @@ static int lm355x_probe(struct i2c_client *client, chip->cdev_torch.max_brightness =8 ; chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set; chip->cdev_torch.default_trigger ="torch" ; - err =led_classdev_register ((struct device *) - &client->dev, &chip->cdev_torch); + err =led_classdev_register (&client->dev, &chip->cdev_torch);if (err <0 ) goto err_create_torch_file;/* indicator */
@@ -477,8 +475,7 @@ static int lm355x_probe(struct i2c_client *client,/* indicator pattern control only for LM3556 */ if (id->driver_data ==CHIP_LM3556 ) chip->cdev_indicator.groups = lm355x_indicator_groups; - err =led_classdev_register ((struct device *) - &client->dev, &chip->cdev_indicator); + err =led_classdev_register (&client->dev, &chip->cdev_indicator);if (err <0 ) goto err_create_indicator_file;
drivers/leds/leds-lm3642.c +3 -6
@@ -340,8 +340,7 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_flash.brightness_set_blocking = lm3642_strobe_brightness_set; chip->cdev_flash.default_trigger ="flash" ; chip->cdev_flash.groups = lm3642_flash_groups, - err =led_classdev_register ((struct device *) - &client->dev, &chip->cdev_flash); + err =led_classdev_register (&client->dev, &chip->cdev_flash);if (err <0 ) {dev_err (chip->dev,"failed to register flash\n" ); goto err_out;
@@ -353,8 +352,7 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_torch.brightness_set_blocking = lm3642_torch_brightness_set; chip->cdev_torch.default_trigger ="torch" ; chip->cdev_torch.groups = lm3642_torch_groups, - err =led_classdev_register ((struct device *) - &client->dev, &chip->cdev_torch); + err =led_classdev_register (&client->dev, &chip->cdev_torch);if (err <0 ) {dev_err (chip->dev,"failed to register torch\n" ); goto err_create_torch_file;
@@ -365,8 +363,7 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_indicator.max_brightness =8 ; chip->cdev_indicator.brightness_set_blocking = lm3642_indicator_brightness_set; - err =led_classdev_register ((struct device *) - &client->dev, &chip->cdev_indicator); + err =led_classdev_register (&client->dev, &chip->cdev_indicator);if (err <0 ) {dev_err (chip->dev,"failed to register indicator\n" ); goto err_create_indicator_file;
Signature · 1 line
2.26.2
Quoted 10 lines · Johan Hovold
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Shouldn't MFD take reference count for their children?
Signature · 2 lines
With Best Regards, Andy Shevchenko
Quoted 5 lines · Andy Shevchenko
Quoted 4 lines · Johan Hovold
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
Quoted 8 lines
Quoted 6 lines
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Shouldn't MFD take reference count for their children?
That's not the issue here. The child driver is allocating memory for the class device (for example using devres), and that will end up being freed on unbind while said device is still registered. The child driver may then even be unloaded. No extra reference can fix this.
Johan
Quoted 14 lines · Johan Hovold
Quoted 12 lines · Andy Shevchenko
Quoted 10 lines · Johan Hovold
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Quoted 6 lines
Quoted 1 line
Shouldn't MFD take reference count for their children?
That's not the issue here. The child driver is allocating memory for the class device (for example using devres), and that will end up being freed on unbind while said device is still registered. The child driver may then even be unloaded. No extra reference can fix this.
Okay, I didn't still get how dropping devres will help here.
Say, we have
->probe()
{
return devm_foo_register();
}
and no ->remove()
vs.
->probe()
{
return foo_register();
}
->remove()
{
foo_unregister();
}
So, basically what you seem to workaround is that ->remove() is not getting called?
Signature · 2 lines
With Best Regards, Andy Shevchenko
Quoted 40 lines · Andy Shevchenko
Quoted 14 lines · Johan Hovold
Quoted 12 lines · Andy Shevchenko
Quoted 10 lines · Johan Hovold
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Quoted 6 lines
Quoted 1 line
Shouldn't MFD take reference count for their children?
That's not the issue here. The child driver is allocating memory for the class device (for example using devres), and that will end up being freed on unbind while said device is still registered. The child driver may then even be unloaded. No extra reference can fix this.
Okay, I didn't still get how dropping devres will help here.
Say, we have
->probe()
{
returndevm_foo_register();
}and no ->remove()
vs.
->probe()
{
returnfoo_register();
}
Quoted 7 lines
->remove()
{
foo_unregister();
}So, basically what you seem to workaround is that ->remove() is not getting called?
Any driver which frees a resource before making sure it's no longer used it is just plain broken. Unfortunately, devres makes this harder to reason about and people get it wrong. This is roughly the current situation with these drivers:
drv->probe(dev) foo = devm_kzalloc(dev); devm_foo_register(dev->parent, foo); // NOTE: dev->parent drv->remove(dev) devres_release_all(dev) kfree(foo); // foo still registered
but foo remains registered until the parent driver is unbound.
Johan
Quoted 53 lines · Johan Hovold
Quoted 39 lines · Andy Shevchenko
Quoted 13 lines · Johan Hovold
Quoted 12 lines · Andy Shevchenko
Quoted 10 lines · Johan Hovold
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Quoted 6 lines
Quoted 1 line
Shouldn't MFD take reference count for their children?
That's not the issue here. The child driver is allocating memory for the class device (for example using devres), and that will end up being freed on unbind while said device is still registered. The child driver may then even be unloaded. No extra reference can fix this.
Okay, I didn't still get how dropping devres will help here.
Say, we have
->probe()
{
returndevm_foo_register();
}and no ->remove()
vs.
->probe()
{
returnfoo_register();
}Quoted 7 lines
->remove()
{
foo_unregister();
}So, basically what you seem to workaround is that ->remove() is not getting called?
Any driver which frees a resource before making sure it's no longer used it is just plain broken. Unfortunately, devres makes this harder to reason about and people get it wrong. This is roughly the current situation with these drivers:
Quoted 4 lines
drv->probe(dev)
foo = devm_kzalloc(dev);
devm_foo_register(dev->parent, foo); // NOTE: dev->parentA-ha! Thanks for this detail.
But why are they doing so?
Quoted 5 lines
drv->remove(dev)
devres_release_all(dev)
kfree(foo); // foo still registeredbut foo remains registered until the parent driver is unbound.
Since the last fixes against kobject elimination, shouldn't be this simple fixed by not supplying dev->parent above?
Signature · 2 lines
With Best Regards, Andy Shevchenko
Quoted 30 lines · Andy Shevchenko
Quoted 29 lines · Johan Hovold
Quoted 14 lines · Andy Shevchenko
Quoted 13 lines · Johan Hovold
Quoted 12 lines · Andy Shevchenko
Quoted 10 lines · Johan Hovold
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Quoted 8 lines
Quoted 6 lines
Quoted 1 line
Shouldn't MFD take reference count for their children?
That's not the issue here. The child driver is allocating memory for the class device (for example using devres), and that will end up being freed on unbind while said device is still registered. The child driver may then even be unloaded. No extra reference can fix this.
Okay, I didn't still get how dropping devres will help here.
Any driver which frees a resource before making sure it's no longer used it is just plain broken. Unfortunately, devres makes this harder to reason about and people get it wrong. This is roughly the current situation with these drivers:
Quoted 7 lines
Quoted 4 lines
drv->probe(dev) foo = devm_kzalloc(dev); devm_foo_register(dev->parent, foo); // NOTE: dev->parentA-ha! Thanks for this detail.
But why are they doing so?
As I mentioned in a commit message, we have quite a few MFD drivers that register class devices under the parent device directly and have been doing so for a very long time.
As this is reflected in sysfs and we may have users relying on the current topology, changing this shouldn't be taken too lightly (drivers may also depend on it). And in any case, it wouldn't be stable material to fix the regressions at hand.
Quoted 8 lines
Quoted 5 lines
drv->remove(dev)
devres_release_all(dev)
kfree(foo); // foo still registeredbut foo remains registered until the parent driver is unbound.
Since the last fixes against kobject elimination, shouldn't be this simple fixed by not supplying
dev->parentabove?
No, that's a separate issue as it also changes the device tree.
Johan
Quoted 5 lines · Johan Hovold
Quoted 4 lines · Andy Shevchenko
Quoted 3 lines · Johan Hovold
Quoted 2 lines · Andy Shevchenko
Quoted 1 line · Johan Hovold
On Mon, Jun 01, 2020 at 04:51:01PM +0300, Andy Shevchenko wrote:
...
Quoted 20 lines
Quoted 12 lines
Quoted 12 lines
Quoted 6 lines
Quoted 4 lines
That's not the issue here. The child driver is allocating memory for the class device (for example using devres), and that will end up being freed on unbind while said device is still registered. The child driver may then even be unloaded. No extra reference can fix this.
Okay, I didn't still get how dropping devres will help here.
Any driver which frees a resource before making sure it's no longer used it is just plain broken. Unfortunately, devres makes this harder to reason about and people get it wrong. This is roughly the current situation with these drivers:
Quoted 7 lines
Quoted 4 lines
drv->probe(dev) foo = devm_kzalloc(dev); devm_foo_register(dev->parent, foo); // NOTE: dev->parentA-ha! Thanks for this detail.
But why are they doing so?
Quoted 8 lines
As I mentioned in a commit message, we have quite a few MFD drivers that register class devices under the parent device directly and have been doing so for a very long time.
As this is reflected in sysfs and we may have users relying on the current topology, changing this shouldn't be taken too lightly (drivers may also depend on it). And in any case, it wouldn't be stable material to fix the regressions at hand.
I see.
Quoted 10 lines
Quoted 8 lines
Quoted 5 lines
drv->remove(dev)
devres_release_all(dev)
kfree(foo); // foo still registeredbut foo remains registered until the parent driver is unbound.
Since the last fixes against kobject elimination, shouldn't be this simple fixed by not supplying
dev->parentabove?No, that's a separate issue as it also changes the device tree.
Thanks for elaboration.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Hi!
Quoted 10 lines
Several MFD child drivers register their class devices directly under the parent device (about half of the MFD LED drivers do so).
This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered (and, for example, oopses on later parent MFD driver unbind or LED class callbacks, or resource leaks and name clashes on child driver reload).
Quoted 4 lines
Included is also a clean up removing some pointless casts when registering class devices.
All but the lm3533 one have only been compile tested.
It would be nicer to have devres framework work with these... but I guess this should go in...
Best regards,
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html