[PATCH v2] iio: light: isl29028: fix runtime PM reference leak on error paths
From: Fabio Cesari
Date: Sun Sep 06 2026 - 18:40:49 EST
Both isl29028_read_raw() and isl29028_write_raw() take a runtime PM
reference with pm_runtime_resume_and_get() and are supposed to drop it
again with pm_runtime_put_autosuspend() before returning. On their error
paths they return directly instead, leaking the reference.
The usage count is then never balanced, so the device stops entering
autosuspend for the rest of its lifetime. The effect accumulates: every
failed access leaks another reference.
In isl29028_write_raw() this is reachable from userspace with a single
rejected sysfs write, for example
echo 200 > in_proximity_sampling_frequency
which is outside the accepted [1:100] range. It returns -EINVAL with the
reference still held. In isl29028_read_raw() the leak is reached when
the underlying regmap access fails.
Take the reference with PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND()
instead. It is then released when the function returns, on every path
and without an explicit call, so no error path added later can leak it
again.
This also stops the result of pm_runtime_put_autosuspend() from reaching
userspace, which fixes a second problem: that value reports whether the
device could be suspended right away, so -EAGAIN or -EBUSY turned a
successful access into a failure. With CONFIG_PM=n it is a stub
returning -ENOSYS, so every read and write fails today.
PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND() is v6.19 and later, so this
does not apply as-is to older trees. The adjustment there is to keep
pm_runtime_resume_and_get() and drop the reference on the way out with
an unchecked pm_runtime_put_autosuspend(), which fixes both the leak and
the return value.
Fixes: 2db5054ac28d ("staging: iio: isl29028: add runtime power management support")
Suggested-by: Joshua Crofts <joshua.crofts1@xxxxxxxxx>
Cc: <stable@xxxxxxxxxxxxxxx> # see patch description, needs adjustments for < 6.19
Assisted-by: LLM coccinelle
Signed-off-by: Fabio Cesari <fabio.cesari@xxxxxxxxx>
---
Changes in v2, from the review of v1:
- take the reference with PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND()
instead of balancing pm_runtime_put_autosuspend() by hand, so that its
return value no longer reaches userspace either
- use the Assisted-by form documented in coding-assistants.rst
- move the note on how the bug was found below the ---
v1: https://lore.kernel.org/linux-iio/20260906131203.125407-1-fabio.cesari@xxxxxxxxx/
Found by auditing IIO drivers with a Coccinelle semantic patch for
runtime PM acquire/release imbalances.
Compile-tested only: arm64 (native) and x86_64 (cross), defconfig plus
CONFIG_SENSORS_ISL29028=m, plus an arm64 CONFIG_PM=n build to cover the
stubs, with gcc 15.2.0, W=1 and sparse v0.6.5-rc1: no warnings. I have no
isl29028 hardware, so this is untested at runtime.
drivers/iio/light/isl29028.c | 33 ++++++++-------------------------
1 file changed, 8 insertions(+), 25 deletions(-)
diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c
index 33deb1726689..03ad48930231 100644
--- a/drivers/iio/light/isl29028.c
+++ b/drivers/iio/light/isl29028.c
@@ -342,8 +342,9 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
struct device *dev = regmap_get_device(chip->regmap);
int ret;
- ret = pm_runtime_resume_and_get(dev);
- if (ret < 0)
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
return ret;
mutex_lock(&chip->lock);
@@ -392,14 +393,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
mutex_unlock(&chip->lock);
- if (ret < 0)
- return ret;
-
- ret = pm_runtime_put_autosuspend(dev);
- if (ret < 0)
- return ret;
-
- return 0;
+ return ret;
}
static int isl29028_read_raw(struct iio_dev *indio_dev,
@@ -408,10 +402,11 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
{
struct isl29028_chip *chip = iio_priv(indio_dev);
struct device *dev = regmap_get_device(chip->regmap);
- int ret, pm_ret;
+ int ret;
- ret = pm_runtime_resume_and_get(dev);
- if (ret < 0)
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
return ret;
mutex_lock(&chip->lock);
@@ -461,18 +456,6 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&chip->lock);
- if (ret < 0)
- return ret;
-
- /**
- * Preserve the ret variable if the call to
- * pm_runtime_put_autosuspend() is successful so the reading
- * (if applicable) is returned to user space.
- */
- pm_ret = pm_runtime_put_autosuspend(dev);
- if (pm_ret < 0)
- return pm_ret;
-
return ret;
}
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.53.0