Linux Kernel Mentees list
 help / color / mirror / Atom feed
From: Marco Chen <marcochen.dev@gmail.com>
To: jic23@kernel.org
Cc: dlechner@baylibre.com, nuno.sa@analog.com, andy@kernel.org,
	pmeerw@pmeerw.net, matt@ranostay.sg, linux-iio@vger.kernel.org,
	linux-kernel@vger.kernel.org, skhan@linuxfoundation.org,
	linux-kernel-mentees@lists.linux.dev
Subject: [PATCH v2] iio: health: max30102: fix NULL dereference in interrupt handler
Date: Tue,  4 Aug 2026 19:43:55 -0400	[thread overview]
Message-ID: <20260804234355.65319-1-marcochen.dev@gmail.com> (raw)

The interrupt is requested in max30102_probe() and stays enabled
for the lifetime of the device, but indio_dev->active_scan_mask is only
valid while a buffer is enabled. When an interrupt arrives while no
buffer is enabled, the handler dereferences the NULL active_scan_mask:

  Unable to handle kernel NULL pointer dereference at virtual address
0000000000000000
  pc : __bitmap_weight+0x64/0x98
  lr : max30102_interrupt_handler+0x48/0x160 [max30102]
  Call trace:
   __bitmap_weight+0x64/0x98 (P)
   max30102_interrupt_handler+0x48/0x160 [max30102]
   irq_thread_fn+0x28/0xa8
   irq_thread+0x184/0x30c
   kthread+0x118/0x124
   ret_from_fork+0x10/0x20

Read the interrupt status register at the top of the handler. If
FIFO_RDY is not set, return early because it isn't our interrupt.
Because FIFO_RDY is the only interrupt source enabled by this driver in
max30102_chip_init(), an invocation of max30102_interrupt_handler()
without FIFO_RDY set carries no data to read and can return early before
ever accessing active_scan_mask. This status read also deasserts the
MAX30102's active-low interrupt pin.

Since the interrupt status register is now read at the top of the
handler, pass the status value into max30102_fifo_count() instead of
having it read the register a second time.

Fixes: 90579b69e94b ("iio: health: max30102: Add MAX30105 support")
Signed-off-by: Marco Chen <marcochen.dev@gmail.com>
---
Changes in v2:
- Check FIFO_RDY bit in the interrupt status register instead of 
  active_scan_mask, because that was racy in v1, as pointed out by 
  Jonathan and David.
- Read interrupt status once and pass it into max30102_fifo_count().
- Check the regmap_read() return value and log on failure.

Tested with a MAX30102 on a Raspberry Pi 4 over I2C.
v1: https://lore.kernel.org/linux-iio/20260731184124.112124-1-marcochen.dev@gmail.com/

 drivers/iio/health/max30102.c | 37 +++++++++++++++++++++++------------
 1 file changed, 25 insertions(+), 12 deletions(-)

diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index c37316c86f14..b949ce36c248 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -235,17 +235,10 @@ static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = {
 	.predisable = max30102_buffer_predisable,
 };
 
-static inline int max30102_fifo_count(struct max30102_data *data)
+static inline int max30102_fifo_count(unsigned int status)
 {
-	unsigned int val;
-	int ret;
-
-	ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS, &val);
-	if (ret)
-		return ret;
-
 	/* FIFO has one sample slot left */
-	if (val & MAX30102_REG_INT_STATUS_FIFO_RDY)
+	if (status & MAX30102_REG_INT_STATUS_FIFO_RDY)
 		return 1;
 
 	return 0;
@@ -290,19 +283,39 @@ static irqreturn_t max30102_interrupt_handler(int irq, void *private)
 {
 	struct iio_dev *indio_dev = private;
 	struct max30102_data *data = iio_priv(indio_dev);
-	unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask,
-						  iio_get_masklength(indio_dev));
+	unsigned int measurements, status;
 	int ret, cnt = 0;
 
+	ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS, &status);
+	if (ret) {
+		dev_err_ratelimited(&data->client->dev,
+				    "Failed to read IRQ status: %d\n", ret);
+		return IRQ_HANDLED;
+	}
+	if (!(status & MAX30102_REG_INT_STATUS_FIFO_RDY))
+		return IRQ_HANDLED;
+
+	measurements = bitmap_weight(indio_dev->active_scan_mask,
+				     iio_get_masklength(indio_dev));
+
 	mutex_lock(&data->lock);
 
-	while (cnt || (cnt = max30102_fifo_count(data)) > 0) {
+	while (cnt || (cnt = max30102_fifo_count(status)) > 0) {
 		ret = max30102_read_measurement(data, measurements);
 		if (ret)
 			break;
 
 		iio_push_to_buffers(data->indio_dev, data->processed_buffer);
 		cnt--;
+
+		ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS,
+				  &status);
+		if (ret) {
+			dev_err_ratelimited(&data->client->dev,
+					    "Failed to read IRQ status: %d\n",
+					    ret);
+			break;
+		}
 	}
 
 	mutex_unlock(&data->lock);
-- 
2.55.0


             reply	other threads:[~2026-08-04 23:44 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-04 23:43 Marco Chen [this message]
2026-08-05  0:58 ` [PATCH v2] iio: health: max30102: fix NULL dereference in interrupt handler Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260804234355.65319-1-marcochen.dev@gmail.com \
    --to=marcochen.dev@gmail.com \
    --cc=andy@kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=jic23@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel-mentees@lists.linux.dev \
    --cc=linux-kernel@vger.kernel.org \
    --cc=matt@ranostay.sg \
    --cc=nuno.sa@analog.com \
    --cc=pmeerw@pmeerw.net \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox