| From 124a69d5ac0eb2df1a4f5cabf15ecbc015c78331 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sat, 20 Dec 2025 23:35:59 +0100 |
| Subject: power: supply: bq25980: Fix use-after-free in power_supply_changed() |
| |
| From: Waqar Hameed <waqar.hameed@axis.com> |
| |
| [ Upstream commit 5f0b1cb41906e86b64bf69f5ededb83b0d757c27 ] |
| |
| Using the `devm_` variant for requesting IRQ _before_ the `devm_` |
| variant for allocating/registering the `power_supply` handle, means that |
| the `power_supply` handle will be deallocated/unregistered _before_ the |
| interrupt handler (since `devm_` naturally deallocates in reverse |
| allocation order). This means that during removal, there is a race |
| condition where an interrupt can fire just _after_ the `power_supply` |
| handle has been freed, *but* just _before_ the corresponding |
| unregistration of the IRQ handler has run. |
| |
| This will lead to the IRQ handler calling `power_supply_changed()` with |
| a freed `power_supply` handle. Which usually crashes the system or |
| otherwise silently corrupts the memory... |
| |
| Note that there is a similar situation which can also happen during |
| `probe()`; the possibility of an interrupt firing _before_ registering |
| the `power_supply` handle. This would then lead to the nasty situation |
| of using the `power_supply` handle *uninitialized* in |
| `power_supply_changed()`. |
| |
| Fix this racy use-after-free by making sure the IRQ is requested _after_ |
| the registration of the `power_supply` handle. |
| |
| Fixes: 5069185fc18e ("power: supply: bq25980: Add support for the BQ259xx family") |
| Signed-off-by: Waqar Hameed <waqar.hameed@axis.com> |
| Link: https://patch.msgid.link/8763035cadb959e14787b3837f2d3db61f6e1c34.1766268280.git.waqar.hameed@axis.com |
| Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/power/supply/bq25980_charger.c | 12 ++++++------ |
| 1 file changed, 6 insertions(+), 6 deletions(-) |
| |
| diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c |
| index 723858d62d141..73f06f09f134c 100644 |
| --- a/drivers/power/supply/bq25980_charger.c |
| +++ b/drivers/power/supply/bq25980_charger.c |
| @@ -1241,6 +1241,12 @@ static int bq25980_probe(struct i2c_client *client) |
| return ret; |
| } |
| |
| + ret = bq25980_power_supply_init(bq, dev); |
| + if (ret) { |
| + dev_err(dev, "Failed to register power supply\n"); |
| + return ret; |
| + } |
| + |
| if (client->irq) { |
| ret = devm_request_threaded_irq(dev, client->irq, NULL, |
| bq25980_irq_handler_thread, |
| @@ -1251,12 +1257,6 @@ static int bq25980_probe(struct i2c_client *client) |
| return ret; |
| } |
| |
| - ret = bq25980_power_supply_init(bq, dev); |
| - if (ret) { |
| - dev_err(dev, "Failed to register power supply\n"); |
| - return ret; |
| - } |
| - |
| ret = bq25980_hw_init(bq); |
| if (ret) { |
| dev_err(dev, "Cannot initialize the chip.\n"); |
| -- |
| 2.51.0 |
| |