power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
cover power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
The majority of the drivers in drivers/power/supply/ do the right thing when registering an interrupt handler and the power_supply handle; namely making sure that the interrupt handler only runs while the power_supply handle is valid. This driver requests the IRQ a little too soon, and this can lead to a nasty NULL pointer dereference as thoroughly explained in the commit message. This patch series also contains some other related clean-ups that makes life a little easier.
This issue was found when writing a new driver for the upcoming TI BQ25630 [1]. Patch adding support for that one will be sent as soon as TI releases the datasheet publicly, which should be anytime soon...
[1] https://www.ti.com/product/BQ25630
Waqar Hameed (3):
power: supply: wm97xx: Fix NULL pointer dereference in
power_supply_changed()
power: supply: wm97xx: Use devm_power_supply_register()
power: supply: wm97xx: Use devm_kcalloc()
drivers/power/supply/wm97xx_battery.c | 40 ++++++++++----------------- 1 file changed, 15 insertions(+), 25 deletions(-)
base-commit: fa084c35afa13ab07a860ef0936cd987f9aa0460
Signature · 1 line
2.39.5
PATCH 1/3 power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
In probe(), request_irq() is called before allocating/registering a power_supply handle. If an interrupt is fired between the call to request_irq() and power_supply_register(), the power_supply handle will be used uninitialized in power_supply_changed() in wm97xx_bat_update() (triggered from the interrupt handler). This will lead to a NULL pointer dereference since
Fix this racy NULL pointer dereference by making sure the IRQ is requested _after_ the registration of the power_supply handle. Since the IRQ is the last thing requests in the probe() now, remove the error path for freeing it. Instead add one for unregistering the power_supply handle when IRQ request fails.
--- drivers/power/supply/wm97xx_battery.c | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-)
drivers/power/supply/wm97xx_battery.c +19 -15
@@ -178,12 +178,6 @@ static int wm97xx_bat_probe(struct platform_device *dev) "failed to get charge GPIO\n");if (charge_gpiod) {gpiod_set_consumer_name (charge_gpiod,"BATT CHRG" ); - ret =request_irq (gpiod_to_irq (charge_gpiod), - wm97xx_chrg_irq,0 , -"AC Detect" , dev); -if (ret) -return dev_err_probe (&dev->dev, ret, -"failed to request GPIO irq\n" ); props++;/* POWER_SUPPLY_PROP_STATUS */ }
@@ -199,10 +193,8 @@ static int wm97xx_bat_probe(struct platform_device *dev) props++;/* POWER_SUPPLY_PROP_VOLTAGE_MIN */ prop =kcalloc (props,sizeof (*prop),GFP_KERNEL ); -if (!prop) { - ret = -ENOMEM ; - goto err3; - } +if (!prop) +return -ENOMEM ; prop[i++] =POWER_SUPPLY_PROP_PRESENT ;if (charge_gpiod)
@@ -236,15 +228,27 @@ static int wm97xx_bat_probe(struct platform_device *dev)schedule_work (&bat_work); }else { ret =PTR_ERR (bat_psy); - goto err4; + goto free; + } + +if (charge_gpiod) { + ret =request_irq (gpiod_to_irq (charge_gpiod), wm97xx_chrg_irq, +0 ,"AC Detect" , dev); +if (ret) { +dev_err_probe (&dev->dev, ret, +"failed to request GPIO irq\n" ); + goto unregister; + } }return 0 ; -err4: + +unregister: +power_supply_unregister (bat_psy); + +free:kfree (prop); -err3: -if (charge_gpiod) -free_irq (gpiod_to_irq (charge_gpiod), dev); +return ret; }
Signature · 1 line
2.39.5
PATCH 2/3 power: supply: wm97xx: Use devm_power_supply_register()
In reply to Waqar HameedInstead of handling the registration manually, use the automatic devres variant devm_power_supply_register(). This is less error prone and cleaner.
--- drivers/power/supply/wm97xx_battery.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
drivers/power/supply/wm97xx_battery.c +2 -6
@@ -223,7 +223,7 @@ static int wm97xx_bat_probe(struct platform_device *dev) bat_psy_desc.properties = prop; bat_psy_desc.num_properties = props; - bat_psy =power_supply_register (&dev->dev, &bat_psy_desc, &cfg); + bat_psy =devm_power_supply_register (&dev->dev, &bat_psy_desc, &cfg);if (!IS_ERR (bat_psy)) {schedule_work (&bat_work); }else {
@@ -237,15 +237,12 @@ static int wm97xx_bat_probe(struct platform_device *dev)if (ret) {dev_err_probe (&dev->dev, ret,"failed to request GPIO irq\n" ); - goto unregister; + goto free; } }return 0 ; -unregister: -power_supply_unregister (bat_psy); - free:kfree (prop);
@@ -257,7 +254,6 @@ static void wm97xx_bat_remove(struct platform_device *dev)if (charge_gpiod)free_irq (gpiod_to_irq (charge_gpiod), dev);cancel_work_sync (&bat_work); -power_supply_unregister (bat_psy);kfree (prop); }
Signature · 1 line
2.39.5
Re: [PATCH 2/3] power: supply: wm97xx: Use devm_power_supply_register()
Hi,
Quoted 40 lines · Waqar Hameed
Instead of handling the registration manually, use the automatic
devresvariantdevm_power_supply_register(). This is less error prone and cleaner.Signed-off-by Waqar Hameed <[email protected]>--- drivers/power/supply/wm97xx_battery.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)drivers/power/supply/wm97xx_battery.c +2
-5@@ -223,7 +223,7 @@ static int wm97xx_bat_probe(struct platform_device *dev) bat_psy_desc.properties = prop; bat_psy_desc.num_properties = props; - bat_psy =power_supply_register (&dev->dev, &bat_psy_desc, &cfg); + bat_psy =devm_power_supply_register (&dev->dev, &bat_psy_desc, &cfg);if (!IS_ERR (bat_psy)) {schedule_work (&bat_work); }else {@@ -237,15 +237,12 @@ static int wm97xx_bat_probe(struct platform_device *dev)if (ret) {dev_err_probe (&dev->dev, ret,"failed to request GPIO irq\n" ); - goto unregister; + goto free; } }return 0 ; -unregister: -power_supply_unregister (bat_psy); - free:kfree (prop);
drivers/power/supply/wm97xx_battery.c static void wm97xx_bat_remove(struct platform_device *dev) · line 258
Quoted 6 lines
drivers/power/supply/wm97xx_battery.c +0
-1@@ -257,7 +254,6 @@ static void wm97xx_bat_remove(struct platform_device *dev)if (charge_gpiod)free_irq (gpiod_to_irq (charge_gpiod), dev);cancel_work_sync (&bat_work); -power_supply_unregister (bat_psy);kfree (prop); }
This free's prop before bat_psy. You fix that in the next patch, but to be bisectable those two patches must be swapped.
Greetings,
-- Sebastian
Re: [PATCH 2/3] power: supply: wm97xx: Use devm_power_supply_register()
On Mon, Jan 12, 2026 at 02:36 +0100 Sebastian Reichel <[email protected]> wrote:
[...]
Quoted 10 lines
drivers/power/supply/wm97xx_battery.c static void wm97xx_bat_remove(struct platform_device *dev) · line 258
Quoted 7 lines
drivers/power/supply/wm97xx_battery.c +0
-1@@ -257,7 +254,6 @@ static void wm97xx_bat_remove(struct platform_device *dev)if (charge_gpiod)free_irq (gpiod_to_irq (charge_gpiod), dev);cancel_work_sync (&bat_work); -power_supply_unregister (bat_psy);kfree (prop); }This free's prop before
bat_psy. You fix that in the next patch, but to be bisectable those two patches must be swapped.
Ah, of course! Let's swap the patches in v2.
PATCH 3/3 power: supply: wm97xx: Use devm_kcalloc()
In reply to Waqar HameedInstead of handling the memory allocation manually, use the automatic devres variant devm_kcalloc(). This is less error prone and eliminates the goto-path.
--- drivers/power/supply/wm97xx_battery.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-)
drivers/power/supply/wm97xx_battery.c +8 -18
@@ -192,7 +192,7 @@ static int wm97xx_bat_probe(struct platform_device *dev)if (pdata->min_voltage >=0 ) props++;/* POWER_SUPPLY_PROP_VOLTAGE_MIN */ - prop =kcalloc (props,sizeof (*prop),GFP_KERNEL ); + prop =devm_kcalloc (&dev->dev, props,sizeof (*prop),GFP_KERNEL );if (!prop)return -ENOMEM ;
@@ -224,29 +224,20 @@ static int wm97xx_bat_probe(struct platform_device *dev) bat_psy_desc.num_properties = props; bat_psy =devm_power_supply_register (&dev->dev, &bat_psy_desc, &cfg); -if (!IS_ERR (bat_psy)) { -schedule_work (&bat_work); - }else { - ret =PTR_ERR (bat_psy); - goto free; - } +if (IS_ERR (bat_psy)) +return PTR_ERR (bat_psy); + +schedule_work (&bat_work);if (charge_gpiod) { ret =request_irq (gpiod_to_irq (charge_gpiod), wm97xx_chrg_irq,0 ,"AC Detect" , dev); -if (ret) { -dev_err_probe (&dev->dev, ret, -"failed to request GPIO irq\n" ); - goto free; - } +if (ret) +return dev_err_probe (&dev->dev, ret, +"failed to request GPIO irq\n" ); }return 0 ; - -free: -kfree (prop); - -return ret; }static void wm97xx_bat_remove (struct platform_device *dev)
@@ -254,7 +245,6 @@ static void wm97xx_bat_remove(struct platform_device *dev)if (charge_gpiod)free_irq (gpiod_to_irq (charge_gpiod), dev);cancel_work_sync (&bat_work); -kfree (prop); }static struct platform_driver wm97xx_bat_driver = {
Signature · 1 line
2.39.5
Sebastian Reichel
AppliedRe: (subset) [PATCH 0/3] power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
In reply to Waqar HameedQuoted 9 lines · Waqar Hameed
The majority of the drivers in
drivers/power/supply/do the right thing when registering an interrupt handler and thepower_supplyhandle; namely making sure that the interrupt handler only runs while thepower_supplyhandle is valid. This driver requests the IRQ a little too soon, and this can lead to a nasty NULL pointer dereference as thoroughly explained in the commit message. This patch series also contains some other related clean-ups that makes life a little easier.[...]
Applied, thanks!
[1/3] power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
commit: 39fe0eac6d755ef215026518985fcf8de9360e9e
Best regards,
Signature · 1 line
Sebastian Reichel <[email protected]>