[PATCH v3 4/6] firmware: samsung: acpm: Validate SRAM shared memory and queue pointers

Tudor Ambarus tudor.ambarus at linaro.org
Wed Apr 29 06:11:53 PDT 2026


Sashiko identified multiple missing validation checks [1].

The ACPM driver reads queue pointers (rx_front, rx_rear, tx_front) and
configuration parameters (qlen) directly from shared SRAM without
verifying their validity. Relying blindly on firmware-provided values
leaves the kernel vulnerable to crashes or infinite loops if the
firmware misbehaves.

This patch fixes three specific vulnerabilities:

1. RX path infinite loop and OOB read: The rear pointer ('i') is used
   to calculate the MMIO address before the modulo operation is applied.
   If 'rx_front' or 'i' are >= achan->qlen, the driver performs an
   out-of-bounds read. Furthermore, because 'i' is mathematically capped
   by the modulo operator, if 'rx_front' is >= qlen, 'i' will never
   equal 'rx_front', causing the CPU to spin forever and deadlock the
   polling thread.
2. TX path out-of-bounds: 'tx_front' is used to calculate queue indices.
   If it exceeds the queue length, it causes invalid state tracking and
   out-of-bounds memory accesses during __iowrite32_copy().
3. Divide-by-zero panics: 'qlen' is read from SRAM during channel
   initialization. If 'qlen' is 0, any subsequent modulo operations
   (% achan->qlen) will trigger a divide-by-zero kernel panic.

Protect the kernel by strictly validating the initialization parameters
and MMIO queue offsets immediately after reading them.

Cc: stable at vger.kernel.org
Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver")
Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1]
Signed-off-by: Tudor Ambarus <tudor.ambarus at linaro.org>
---
 drivers/firmware/samsung/exynos-acpm.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c
index bd0d48e9d157..e4d8d1120192 100644
--- a/drivers/firmware/samsung/exynos-acpm.c
+++ b/drivers/firmware/samsung/exynos-acpm.c
@@ -230,6 +230,13 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
 	rx_front = readl(achan->rx.front);
 	i = readl(achan->rx.rear);
 
+	if (rx_front >= achan->qlen || i >= achan->qlen) {
+		dev_err(achan->acpm->dev,
+			"Invalid RX queue pointers from firmware: front=%u rear=%u qlen=%u\n",
+			rx_front, i, achan->qlen);
+		return -EIO;
+	}
+
 	tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
 
 	if (i == rx_front) {
@@ -439,6 +446,14 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer)
 
 	scoped_guard(mutex, &achan->tx_lock) {
 		tx_front = readl(achan->tx.front);
+
+		if (tx_front >= achan->qlen) {
+			dev_err(achan->acpm->dev,
+				"Invalid TX front pointer from firmware: %u (qlen: %u)\n",
+				tx_front, achan->qlen);
+			return -EIO;
+		}
+
 		idx = (tx_front + 1) % achan->qlen;
 
 		ret = acpm_wait_for_queue_slots(achan, idx);
@@ -574,6 +589,12 @@ static int acpm_channels_init(struct acpm_info *acpm)
 
 		acpm_chan_shmem_get_params(achan, chan_shmem);
 
+		if (!achan->qlen) {
+			dev_err(dev, "Invalid shared memory parameters for channel %d: qlen=%u\n",
+				i, achan->qlen);
+			return -EIO;
+		}
+
 		ret = acpm_achan_alloc_cmds(achan);
 		if (ret)
 			return ret;

-- 
2.54.0.545.g6539524ca2-goog




More information about the linux-arm-kernel mailing list