[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