Skip to contentSkip to conversation

power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()

PATCH Applied Waqar Hameed 2 participants 7 messages Dec 20, 2025 – Jan 14 Power supply

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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.

Fixes 7c87942aef52 ("wm97xx_battery: Use irq to detect charger state")
---
 drivers/power/supply/wm97xx_battery.c | 34 +++++++++++++++------------
 1 file changed, 19 insertions(+), 15 deletions(-)
drivers/power/supply/wm97xx_battery.c +19 -15
index b3b0c37a9dd2d..f00722c88c6fe 100644
@@ -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
#2 lore raw

PATCH 2/3 power: supply: wm97xx: Use devm_power_supply_register()

In reply to Waqar Hameed

Instead 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
index f00722c88c6fe..e91467dcab19c 100644
@@ -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
#3 lore raw

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 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 -5
index f00722c88c6fe..e91467dcab19c 100644
@@ -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

#4 lore raw

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.

#5 lore raw

PATCH 3/3 power: supply: wm97xx: Use devm_kcalloc()

In reply to Waqar Hameed

Instead 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
index e91467dcab19c..a6c55b1e02863 100644
@@ -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
#6 lore raw

Re: (subset) [PATCH 0/3] power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()

In reply to Waqar Hameed
Quoted 9 lines · Waqar Hameed

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.

[...]

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]>