]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
7.0-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 22 Jun 2026 05:35:03 +0000 (07:35 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 22 Jun 2026 05:35:03 +0000 (07:35 +0200)
added patches:
firmware-exynos-acpm-count-acpm_xfer-buffers-with-__counted_by_ptr.patch
firmware-exynos-acpm-count-number-of-commands-in-acpm_xfer.patch
firmware-samsung-acpm-fix-cross-thread-rx-length-corruption.patch
firmware-samsung-acpm-fix-false-timeouts-and-use-after-free-in-polling.patch
firmware-samsung-acpm-fix-missing-lkmm-barriers-in-sequence-allocator.patch

queue-7.0/firmware-exynos-acpm-count-acpm_xfer-buffers-with-__counted_by_ptr.patch [new file with mode: 0644]
queue-7.0/firmware-exynos-acpm-count-number-of-commands-in-acpm_xfer.patch [new file with mode: 0644]
queue-7.0/firmware-samsung-acpm-fix-cross-thread-rx-length-corruption.patch [new file with mode: 0644]
queue-7.0/firmware-samsung-acpm-fix-false-timeouts-and-use-after-free-in-polling.patch [new file with mode: 0644]
queue-7.0/firmware-samsung-acpm-fix-missing-lkmm-barriers-in-sequence-allocator.patch [new file with mode: 0644]
queue-7.0/series

diff --git a/queue-7.0/firmware-exynos-acpm-count-acpm_xfer-buffers-with-__counted_by_ptr.patch b/queue-7.0/firmware-exynos-acpm-count-acpm_xfer-buffers-with-__counted_by_ptr.patch
new file mode 100644 (file)
index 0000000..69b4624
--- /dev/null
@@ -0,0 +1,62 @@
+From stable+bounces-266585-greg=kroah.com@vger.kernel.org Wed Jun 17 00:41:17 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 18:39:35 -0400
+Subject: firmware: exynos-acpm: Count acpm_xfer buffers with __counted_by_ptr
+To: stable@vger.kernel.org
+Cc: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>, Tudor Ambarus <tudor.ambarus@linaro.org>, Krzysztof Kozlowski <krzk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260616223936.3557131-2-sashal@kernel.org>
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+
+[ Upstream commit 951b8eee0581bbf39e7b0464d679eee8cb9da3e0 ]
+
+Use __counted_by_ptr() attribute on the acpm_xfer buffers so UBSAN will
+validate runtime that we do not pass over the buffer size, thus making
+code safer.
+
+Usage of __counted_by_ptr() (or actually __counted_by()) requires that
+counter is initialized before counted array.
+
+Tested-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260219-firmare-acpm-counted-v2-3-e1f7389237d3@oss.qualcomm.com
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Stable-dep-of: f133bd4b5daf ("firmware: samsung: acpm: Fix cross-thread RX length corruption")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/firmware/samsung/exynos-acpm-dvfs.c |    4 ++--
+ drivers/firmware/samsung/exynos-acpm.h      |    4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/firmware/samsung/exynos-acpm-dvfs.c
++++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c
+@@ -25,12 +25,12 @@ static void acpm_dvfs_set_xfer(struct ac
+                              unsigned int acpm_chan_id, bool response)
+ {
+       xfer->acpm_chan_id = acpm_chan_id;
+-      xfer->txd = cmd;
+       xfer->txcnt = cmdlen;
++      xfer->txd = cmd;
+       if (response) {
+-              xfer->rxd = cmd;
+               xfer->rxcnt = cmdlen;
++              xfer->rxd = cmd;
+       }
+ }
+--- a/drivers/firmware/samsung/exynos-acpm.h
++++ b/drivers/firmware/samsung/exynos-acpm.h
+@@ -8,8 +8,8 @@
+ #define __EXYNOS_ACPM_H__
+ struct acpm_xfer {
+-      const u32 *txd;
+-      u32 *rxd;
++      const u32 *txd __counted_by_ptr(txcnt);
++      u32 *rxd __counted_by_ptr(rxcnt);
+       size_t txcnt;
+       size_t rxcnt;
+       unsigned int acpm_chan_id;
diff --git a/queue-7.0/firmware-exynos-acpm-count-number-of-commands-in-acpm_xfer.patch b/queue-7.0/firmware-exynos-acpm-count-number-of-commands-in-acpm_xfer.patch
new file mode 100644 (file)
index 0000000..eacf736
--- /dev/null
@@ -0,0 +1,199 @@
+From stable+bounces-266584-greg=kroah.com@vger.kernel.org Wed Jun 17 00:41:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 18:39:34 -0400
+Subject: firmware: exynos-acpm: Count number of commands in acpm_xfer
+To: stable@vger.kernel.org
+Cc: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>, Tudor Ambarus <tudor.ambarus@linaro.org>, Krzysztof Kozlowski <krzk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260616223936.3557131-1-sashal@kernel.org>
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+
+[ Upstream commit 00808ae2e679a97dccc5cf0ee4474ba1e2e8a21a ]
+
+Struct acpm_xfer holds two buffers with u32 commands - rxd and txd - and
+counts their size by rxlen and txlen.  "len" suffix is here ambiguous,
+so could mean length of the buffer or length of commands, and these are
+not the same since each command is u32.  Rename these to rxcnt and
+txcnt, and change their usage to count the number of commands in each
+buffer.
+
+This will have a benefit of allowing to use __counted_by_ptr later.
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Link: https://patch.msgid.link/20260219-firmare-acpm-counted-v2-2-e1f7389237d3@oss.qualcomm.com
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Stable-dep-of: f133bd4b5daf ("firmware: samsung: acpm: Fix cross-thread RX length corruption")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/firmware/samsung/exynos-acpm-dvfs.c |    9 +++++----
+ drivers/firmware/samsung/exynos-acpm-pmic.c |   14 +++++++-------
+ drivers/firmware/samsung/exynos-acpm.c      |   14 +++++++-------
+ drivers/firmware/samsung/exynos-acpm.h      |    4 ++--
+ 4 files changed, 21 insertions(+), 20 deletions(-)
+
+--- a/drivers/firmware/samsung/exynos-acpm-dvfs.c
++++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c
+@@ -5,6 +5,7 @@
+  * Copyright 2025 Linaro Ltd.
+  */
++#include <linux/array_size.h>
+ #include <linux/bitfield.h>
+ #include <linux/firmware/samsung/exynos-acpm-protocol.h>
+ #include <linux/ktime.h>
+@@ -25,11 +26,11 @@ static void acpm_dvfs_set_xfer(struct ac
+ {
+       xfer->acpm_chan_id = acpm_chan_id;
+       xfer->txd = cmd;
+-      xfer->txlen = cmdlen;
++      xfer->txcnt = cmdlen;
+       if (response) {
+               xfer->rxd = cmd;
+-              xfer->rxlen = cmdlen;
++              xfer->rxcnt = cmdlen;
+       }
+ }
+@@ -50,7 +51,7 @@ int acpm_dvfs_set_rate(struct acpm_handl
+       u32 cmd[4];
+       acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate);
+-      acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, false);
++      acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false);
+       return acpm_do_xfer(handle, &xfer);
+ }
+@@ -70,7 +71,7 @@ unsigned long acpm_dvfs_get_rate(struct
+       int ret;
+       acpm_dvfs_init_get_rate_cmd(cmd, clk_id);
+-      acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, true);
++      acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true);
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+--- a/drivers/firmware/samsung/exynos-acpm-pmic.c
++++ b/drivers/firmware/samsung/exynos-acpm-pmic.c
+@@ -63,8 +63,8 @@ static void acpm_pmic_set_xfer(struct ac
+ {
+       xfer->txd = cmd;
+       xfer->rxd = cmd;
+-      xfer->txlen = cmdlen;
+-      xfer->rxlen = cmdlen;
++      xfer->txcnt = cmdlen;
++      xfer->rxcnt = cmdlen;
+       xfer->acpm_chan_id = acpm_chan_id;
+ }
+@@ -86,7 +86,7 @@ int acpm_pmic_read_reg(struct acpm_handl
+       int ret;
+       acpm_pmic_init_read_cmd(cmd, type, reg, chan);
+-      acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
++      acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+@@ -119,7 +119,7 @@ int acpm_pmic_bulk_read(struct acpm_hand
+               return -EINVAL;
+       acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);
+-      acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
++      acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+@@ -159,7 +159,7 @@ int acpm_pmic_write_reg(struct acpm_hand
+       int ret;
+       acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);
+-      acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
++      acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+@@ -199,7 +199,7 @@ int acpm_pmic_bulk_write(struct acpm_han
+               return -EINVAL;
+       acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);
+-      acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
++      acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+@@ -229,7 +229,7 @@ int acpm_pmic_update_reg(struct acpm_han
+       int ret;
+       acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);
+-      acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
++      acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id);
+       ret = acpm_do_xfer(handle, &xfer);
+       if (ret)
+--- a/drivers/firmware/samsung/exynos-acpm.c
++++ b/drivers/firmware/samsung/exynos-acpm.c
+@@ -205,7 +205,7 @@ static void acpm_get_saved_rx(struct acp
+       rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]);
+       if (rx_seqnum == tx_seqnum) {
+-              memcpy(xfer->rxd, rx_data->cmd, xfer->rxlen);
++              memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd));
+               clear_bit(rx_seqnum - 1, achan->bitmap_seqnum);
+       }
+ }
+@@ -258,8 +258,7 @@ static int acpm_get_rx(struct acpm_chan
+               if (rx_data->response) {
+                       if (rx_seqnum == tx_seqnum) {
+-                              __ioread32_copy(xfer->rxd, addr,
+-                                              xfer->rxlen / 4);
++                              __ioread32_copy(xfer->rxd, addr, xfer->rxcnt);
+                               rx_set = true;
+                               clear_bit(seqnum, achan->bitmap_seqnum);
+                       } else {
+@@ -269,8 +268,7 @@ static int acpm_get_rx(struct acpm_chan
+                                * clear yet the bitmap. It will be cleared
+                                * after the response is copied to the request.
+                                */
+-                              __ioread32_copy(rx_data->cmd, addr,
+-                                              xfer->rxlen / 4);
++                              __ioread32_copy(rx_data->cmd, addr, xfer->rxcnt);
+                       }
+               } else {
+                       clear_bit(seqnum, achan->bitmap_seqnum);
+@@ -425,7 +423,9 @@ int acpm_do_xfer(struct acpm_handle *han
+       achan = &acpm->chans[xfer->acpm_chan_id];
+-      if (!xfer->txd || xfer->txlen > achan->mlen || xfer->rxlen > achan->mlen)
++      if (!xfer->txd ||
++          (xfer->txcnt * sizeof(*xfer->txd) > achan->mlen) ||
++          (xfer->rxcnt * sizeof(*xfer->rxd) > achan->mlen))
+               return -EINVAL;
+       if (!achan->poll_completion) {
+@@ -448,7 +448,7 @@ int acpm_do_xfer(struct acpm_handle *han
+               /* Write TX command. */
+               __iowrite32_copy(achan->tx.base + achan->mlen * tx_front,
+-                               xfer->txd, xfer->txlen / 4);
++                               xfer->txd, xfer->txcnt);
+               /* Advance TX front. */
+               writel(idx, achan->tx.front);
+--- a/drivers/firmware/samsung/exynos-acpm.h
++++ b/drivers/firmware/samsung/exynos-acpm.h
+@@ -10,8 +10,8 @@
+ struct acpm_xfer {
+       const u32 *txd;
+       u32 *rxd;
+-      size_t txlen;
+-      size_t rxlen;
++      size_t txcnt;
++      size_t rxcnt;
+       unsigned int acpm_chan_id;
+ };
diff --git a/queue-7.0/firmware-samsung-acpm-fix-cross-thread-rx-length-corruption.patch b/queue-7.0/firmware-samsung-acpm-fix-cross-thread-rx-length-corruption.patch
new file mode 100644 (file)
index 0000000..c33e6fe
--- /dev/null
@@ -0,0 +1,119 @@
+From stable+bounces-266586-greg=kroah.com@vger.kernel.org Wed Jun 17 00:41:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 18:39:36 -0400
+Subject: firmware: samsung: acpm: Fix cross-thread RX length corruption
+To: stable@vger.kernel.org
+Cc: Tudor Ambarus <tudor.ambarus@linaro.org>, Titouan Ameline de Cadeville <titouan.ameline@gmail.com>, Krzysztof Kozlowski <krzk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260616223936.3557131-3-sashal@kernel.org>
+
+From: Tudor Ambarus <tudor.ambarus@linaro.org>
+
+[ Upstream commit f133bd4b5daf71bccdde0ad1a4f47fac76a6bfb1 ]
+
+Sashiko identified a cross-thread RX length corruption bug when
+reviewing the thermal addition to ACPM [1].
+
+When multiple threads concurrently send IPC requests, the ACPM polling
+mechanism can encounter responses belonging to other threads. To drain
+the queue, the driver saves these concurrent responses into an internal
+cache (`rx_data->cmd`) to be retrieved later by the owning thread.
+
+Previously, the driver incorrectly used `xfer->rxcnt` (the expected
+receive length of the *current* polling thread) when copying data for
+*other* threads into this cache. If the threads expected responses of
+different lengths, this resulted in buffer underflows (leading to reads
+of uninitialized memory) or potential buffer overflows.
+
+Fix this by replacing the boolean `response` flag in
+`struct acpm_rx_data` with `rxcnt`, caching the exact expected receive
+length for each specific transaction during transfer preparation. Use
+this cached length when saving concurrent responses.
+
+Consequently, ensure that `xfer->rxcnt` is explicitly zeroed in driver
+helpers (e.g., `acpm_dvfs_set_xfer`) for fire-and-forget messages to
+prevent uninitialized stack garbage from being interpreted as a massive
+expected receive length.
+
+Cc: stable@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]
+Reported-by: Titouan Ameline de Cadeville <titouan.ameline@gmail.com>
+Closes: https://lore.kernel.org/r/20260426210255.73674-1-titouan.ameline@gmail.com/
+Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-1-43b5ee7f1674@linaro.org
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/firmware/samsung/exynos-acpm-dvfs.c |    3 +++
+ drivers/firmware/samsung/exynos-acpm.c      |   15 ++++++++-------
+ 2 files changed, 11 insertions(+), 7 deletions(-)
+
+--- a/drivers/firmware/samsung/exynos-acpm-dvfs.c
++++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c
+@@ -31,6 +31,9 @@ static void acpm_dvfs_set_xfer(struct ac
+       if (response) {
+               xfer->rxcnt = cmdlen;
+               xfer->rxd = cmd;
++      } else {
++              xfer->rxcnt = 0;
++              xfer->rxd = NULL;
+       }
+ }
+--- a/drivers/firmware/samsung/exynos-acpm.c
++++ b/drivers/firmware/samsung/exynos-acpm.c
+@@ -104,12 +104,12 @@ struct acpm_queue {
+  *
+  * @cmd:      pointer to where the data shall be saved.
+  * @n_cmd:    number of 32-bit commands.
+- * @response: true if the client expects the RX data.
++ * @rxcnt:    expected length of the response in 32-bit words.
+  */
+ struct acpm_rx_data {
+       u32 *cmd;
+       size_t n_cmd;
+-      bool response;
++      size_t rxcnt;
+ };
+ #define ACPM_SEQNUM_MAX    64
+@@ -199,7 +199,7 @@ static void acpm_get_saved_rx(struct acp
+       const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1];
+       u32 rx_seqnum;
+-      if (!rx_data->response)
++      if (!rx_data->rxcnt)
+               return;
+       rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]);
+@@ -256,7 +256,7 @@ static int acpm_get_rx(struct acpm_chan
+               seqnum = rx_seqnum - 1;
+               rx_data = &achan->rx_data[seqnum];
+-              if (rx_data->response) {
++              if (rx_data->rxcnt) {
+                       if (rx_seqnum == tx_seqnum) {
+                               __ioread32_copy(xfer->rxd, addr, xfer->rxcnt);
+                               rx_set = true;
+@@ -268,7 +268,8 @@ static int acpm_get_rx(struct acpm_chan
+                                * clear yet the bitmap. It will be cleared
+                                * after the response is copied to the request.
+                                */
+-                              __ioread32_copy(rx_data->cmd, addr, xfer->rxcnt);
++                              __ioread32_copy(rx_data->cmd, addr,
++                                              rx_data->rxcnt);
+                       }
+               } else {
+                       clear_bit(seqnum, achan->bitmap_seqnum);
+@@ -380,8 +381,8 @@ static void acpm_prepare_xfer(struct acp
+       /* Clear data for upcoming responses */
+       rx_data = &achan->rx_data[achan->seqnum - 1];
+       memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd);
+-      if (xfer->rxd)
+-              rx_data->response = true;
++      /* zero means no response expected */
++      rx_data->rxcnt = xfer->rxcnt;
+       /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */
+       set_bit(achan->seqnum - 1, achan->bitmap_seqnum);
diff --git a/queue-7.0/firmware-samsung-acpm-fix-false-timeouts-and-use-after-free-in-polling.patch b/queue-7.0/firmware-samsung-acpm-fix-false-timeouts-and-use-after-free-in-polling.patch
new file mode 100644 (file)
index 0000000..77a0d54
--- /dev/null
@@ -0,0 +1,229 @@
+From stable+bounces-266611-greg=kroah.com@vger.kernel.org Wed Jun 17 03:47:41 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 16 Jun 2026 21:47:27 -0400
+Subject: firmware: samsung: acpm: Fix false timeouts and Use-After-Free in polling
+To: stable@vger.kernel.org
+Cc: Tudor Ambarus <tudor.ambarus@linaro.org>, Krzysztof Kozlowski <krzk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260617014727.3671804-4-sashal@kernel.org>
+
+From: Tudor Ambarus <tudor.ambarus@linaro.org>
+
+[ Upstream commit c889b146478885344a220dd468e5a08de088cbc5 ]
+
+Sashiko identified severe races in the polling state machine [1].
+
+In the ACPM driver's polling mode, threads waited for responses by
+monitoring the globally shared 'bitmap_seqnum'. This caused false
+timeouts because if a thread processed its response and freed the
+sequence number, a concurrent TX thread could immediately reallocate
+it before the polling thread woke up.
+
+Additionally, the driver suffered from a cross-thread Use-After-Free
+(UAF) preemption race. Previously, acpm_get_rx() cleared the sequence
+number of whichever RX message it drained from the hardware queue. This
+meant Thread A could globally free Thread B's sequence slot while
+Thread B was asleep. A new Thread C could then steal the slot,
+overwrite the buffer, and leave Thread B to wake up to corrupted state
+or a timeout.
+
+Fix this by rewriting the polling state machine:
+1. Decouple polling from the global allocator by introducing a per-slot
+   'completed' flag, synchronized via smp_store_release() and
+   smp_load_acquire().
+2. Strip acpm_get_saved_rx() out of acpm_get_rx() to make it a pure
+   queue-draining function. Introduce a 'native_match' boolean argument
+   which evaluates to true only if the thread natively processed its
+   own sequence number during the call. This explicitly informs the
+   polling loop whether it must retrieve its payload from the
+   cross-thread cache.
+3. Centralize the cache fallback and sequence number free (clear_bit)
+   inside the polling loop. Crucially, the free operation now strictly
+   targets the thread's own TX sequence number (xfer->txd[0]), rather
+   than the drained RX sequence number. This enforces strict ownership:
+   a thread only ever frees its own allocated sequence slot, and only
+   at the exact moment it completes its poll, eliminating the UAF
+   window.
+
+Furthermore, explicitly guard the 'native_match' assignment with an
+if (rx_seqnum == tx_seqnum) check, even for zero-length (no payload)
+responses. While an unguarded assignment wouldn't crash (because the
+cache fallback acpm_get_saved_rx() safely returns early on zero-length
+transfers) doing so would "lie" to the state machine. If a thread
+drained the queue and found another thread's zero-length message,
+setting native_match = true would falsely convince the polling loop
+that it natively handled its own response. Maintaining a rigorous state
+machine requires that native_match is only set when a thread explicitly
+processes its own sequence number.
+
+Cc: stable@vger.kernel.org
+Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver")
+Closes: https://sashiko.dev/#/patchset/20260429-acpm-fixes-sashiko-reports-v3-0-47cf74ab09ad%40linaro.org [1]
+Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-5-43b5ee7f1674@linaro.org
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/firmware/samsung/exynos-acpm.c |   68 +++++++++++++++++++++++----------
+ 1 file changed, 48 insertions(+), 20 deletions(-)
+
+--- a/drivers/firmware/samsung/exynos-acpm.c
++++ b/drivers/firmware/samsung/exynos-acpm.c
+@@ -105,11 +105,14 @@ struct acpm_queue {
+  * @cmd:      pointer to where the data shall be saved.
+  * @n_cmd:    number of 32-bit commands.
+  * @rxcnt:    expected length of the response in 32-bit words.
++ * @completed:        flag indicating if the firmware response has been fully
++ *            processed.
+  */
+ struct acpm_rx_data {
+       u32 *cmd;
+       size_t n_cmd;
+       size_t rxcnt;
++      bool completed;
+ };
+ #define ACPM_SEQNUM_MAX    64
+@@ -204,26 +207,28 @@ static void acpm_get_saved_rx(struct acp
+       rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]);
+-      if (rx_seqnum == tx_seqnum) {
++      if (rx_seqnum == tx_seqnum)
+               memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd));
+-              clear_bit(rx_seqnum - 1, achan->bitmap_seqnum);
+-      }
+ }
+ /**
+  * acpm_get_rx() - get response from RX queue.
+  * @achan:    ACPM channel info.
+  * @xfer:     reference to the transfer to get response for.
++ * @native_match: pointer to a boolean set to true if the thread natively
++ *                processed its own sequence number during this call.
+  *
+  * Return: 0 on success, -errno otherwise.
+  */
+-static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer)
++static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer,
++                     bool *native_match)
+ {
+       u32 rx_front, rx_seqnum, tx_seqnum, seqnum;
+       const void __iomem *base, *addr;
+       struct acpm_rx_data *rx_data;
+       u32 i, val, mlen;
+-      bool rx_set = false;
++
++      *native_match = false;
+       guard(mutex)(&achan->rx_lock);
+@@ -232,10 +237,8 @@ static int acpm_get_rx(struct acpm_chan
+       tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
+-      if (i == rx_front) {
+-              acpm_get_saved_rx(achan, xfer, tx_seqnum);
++      if (i == rx_front)
+               return 0;
+-      }
+       base = achan->rx.base;
+       mlen = achan->mlen;
+@@ -259,8 +262,13 @@ static int acpm_get_rx(struct acpm_chan
+               if (rx_data->rxcnt) {
+                       if (rx_seqnum == tx_seqnum) {
+                               __ioread32_copy(xfer->rxd, addr, xfer->rxcnt);
+-                              rx_set = true;
+-                              clear_bit(seqnum, achan->bitmap_seqnum);
++                              /*
++                               * Signal completion to the polling thread.
++                               * Pairs with smp_load_acquire() in polling
++                               * loop.
++                               */
++                              smp_store_release(&rx_data->completed, true);
++                              *native_match = true;
+                       } else {
+                               /*
+                                * The RX data corresponds to another request.
+@@ -270,9 +278,21 @@ static int acpm_get_rx(struct acpm_chan
+                                */
+                               __ioread32_copy(rx_data->cmd, addr,
+                                               rx_data->rxcnt);
++                              /*
++                               * Signal completion to the polling thread.
++                               * Pairs with smp_load_acquire() in polling
++                               * loop.
++                               */
++                              smp_store_release(&rx_data->completed, true);
+                       }
+               } else {
+-                      clear_bit(seqnum, achan->bitmap_seqnum);
++                      /*
++                       * Signal completion to the polling thread.
++                       * Pairs with smp_load_acquire() in polling loop.
++                       */
++                      smp_store_release(&rx_data->completed, true);
++                      if (rx_seqnum == tx_seqnum)
++                              *native_match = true;
+               }
+               i = (i + 1) % achan->qlen;
+@@ -281,13 +301,6 @@ static int acpm_get_rx(struct acpm_chan
+       /* We saved all responses, mark RX empty. */
+       writel(rx_front, achan->rx.rear);
+-      /*
+-       * If the response was not in this iteration of the queue, check if the
+-       * RX data was previously saved.
+-       */
+-      if (!rx_set)
+-              acpm_get_saved_rx(achan, xfer, tx_seqnum);
+-
+       return 0;
+ }
+@@ -302,6 +315,7 @@ static int acpm_dequeue_by_polling(struc
+                                  const struct acpm_xfer *xfer)
+ {
+       struct device *dev = achan->acpm->dev;
++      bool native_match;
+       ktime_t timeout;
+       u32 seqnum;
+       int ret;
+@@ -310,12 +324,25 @@ static int acpm_dequeue_by_polling(struc
+       timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US);
+       do {
+-              ret = acpm_get_rx(achan, xfer);
++              ret = acpm_get_rx(achan, xfer, &native_match);
+               if (ret)
+                       return ret;
+-              if (!test_bit(seqnum - 1, achan->bitmap_seqnum))
++              /*
++               * Safely check if our specific transaction has been processed.
++               * smp_load_acquire prevents the CPU from speculatively
++               * executing subsequent instructions before the transaction is
++               * synchronized.
++               */
++              if (smp_load_acquire(&achan->rx_data[seqnum - 1].completed)) {
++                      /* Retrieve payload if another thread cached it for us */
++                      if (!native_match)
++                              acpm_get_saved_rx(achan, xfer, seqnum);
++
++                      /* Relinquish ownership of the sequence slot */
++                      clear_bit(seqnum - 1, achan->bitmap_seqnum);
+                       return 0;
++              }
+               /* Determined experimentally. */
+               udelay(20);
+@@ -380,6 +407,7 @@ static void acpm_prepare_xfer(struct acp
+       /* Clear data for upcoming responses */
+       rx_data = &achan->rx_data[achan->seqnum - 1];
++      rx_data->completed = false;
+       memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd);
+       /* zero means no response expected */
+       rx_data->rxcnt = xfer->rxcnt;
diff --git a/queue-7.0/firmware-samsung-acpm-fix-missing-lkmm-barriers-in-sequence-allocator.patch b/queue-7.0/firmware-samsung-acpm-fix-missing-lkmm-barriers-in-sequence-allocator.patch
new file mode 100644 (file)
index 0000000..abd7450
--- /dev/null
@@ -0,0 +1,105 @@
+From stable+bounces-266788-greg=kroah.com@vger.kernel.org Wed Jun 17 16:28:23 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Jun 2026 10:27:39 -0400
+Subject: firmware: samsung: acpm: Fix missing LKMM barriers in sequence allocator
+To: stable@vger.kernel.org
+Cc: Tudor Ambarus <tudor.ambarus@linaro.org>, Krzysztof Kozlowski <krzk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260617142739.3938338-5-sashal@kernel.org>
+
+From: Tudor Ambarus <tudor.ambarus@linaro.org>
+
+[ Upstream commit bf296f83a3ddab1ab875edc4e8862cb10553064f ]
+
+Sashiko identified memory ordering races in [1].
+
+The ACPM driver uses a globally shared 'bitmap_seqnum' to track
+available sequence numbers. Even though threads now strictly free their
+own sequence numbers, the allocation and freeing of these bits across
+concurrent threads are effectively lockless operations and require
+explicit LKMM memory barriers.
+
+Previously, the driver used plain bitwise operators (test_bit, set_bit,
+clear_bit), which lack ordering guarantees. This creates two race
+conditions on weakly ordered architectures like ARM64:
+
+1. Polling Release Violation: The polling thread copies its payload and
+   calls clear_bit(). Without a release barrier, the CPU can reorder
+   the memory operations, making the cleared bit globally visible
+   before the payload reads have fully completed.
+2. TX Acquire Violation: The TX thread loops on test_bit(), calls
+   set_bit(), and then wipes the payload buffer via memset(). Without
+   an acquire barrier, the CPU can speculatively execute the memset()
+   before the bit is safely and formally claimed.
+
+If these reorderings overlap, a new TX thread can claim the sequence
+number and overwrite the buffer while the original polling thread is
+still actively reading from it.
+
+Fix this by upgrading the bitwise operators. Wrap the TX allocation in
+test_and_set_bit_lock() to establish formal LKMM Acquire semantics, and
+pair it with clear_bit_unlock() in the polling path to enforce Release
+semantics.
+
+Cc: stable@vger.kernel.org
+Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver")
+Closes: https://sashiko.dev/#/patchset/20260423-acpm-fixes-sashiko-reports-v1-0-2217b790925e%40linaro.org [1]
+Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-6-43b5ee7f1674@linaro.org
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/firmware/samsung/exynos-acpm.c |   18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+--- a/drivers/firmware/samsung/exynos-acpm.c
++++ b/drivers/firmware/samsung/exynos-acpm.c
+@@ -7,7 +7,7 @@
+ #include <linux/bitfield.h>
+ #include <linux/bitmap.h>
+-#include <linux/bits.h>
++#include <linux/bitops.h>
+ #include <linux/cleanup.h>
+ #include <linux/container_of.h>
+ #include <linux/delay.h>
+@@ -340,7 +340,7 @@ static int acpm_dequeue_by_polling(struc
+                               acpm_get_saved_rx(achan, xfer, seqnum);
+                       /* Relinquish ownership of the sequence slot */
+-                      clear_bit(seqnum - 1, achan->bitmap_seqnum);
++                      clear_bit_unlock(seqnum - 1, achan->bitmap_seqnum);
+                       return 0;
+               }
+@@ -397,11 +397,18 @@ static void acpm_prepare_xfer(struct acp
+       struct acpm_rx_data *rx_data;
+       u32 *txd = (u32 *)xfer->txd;
+-      /* Prevent chan->seqnum from being re-used */
++      /*
++       * Prevent chan->seqnum from being re-used.
++       * test_and_set_bit_lock() provides formal LKMM Acquire semantics.
++       * It pairs with the RX thread's clear_bit_unlock() to ensure the CPU
++       * does not speculatively execute the rx_data buffer wipe (memset)
++       * before the sequence number is safely claimed.
++       */
+       do {
+               if (++achan->seqnum == ACPM_SEQNUM_MAX)
+                       achan->seqnum = 1;
+-      } while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum));
++              /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */
++      } while (test_and_set_bit_lock(achan->seqnum - 1, achan->bitmap_seqnum));
+       txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum);
+@@ -411,9 +418,6 @@ static void acpm_prepare_xfer(struct acp
+       memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd);
+       /* zero means no response expected */
+       rx_data->rxcnt = xfer->rxcnt;
+-
+-      /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */
+-      set_bit(achan->seqnum - 1, achan->bitmap_seqnum);
+ }
+ /**
index d9a77aef582c186fa30b88ffa77246008d626da4..19401114521703be6aa9b186dce36f36ceb0dd81 100644 (file)
@@ -1,3 +1,8 @@
 io_uring-net-avoid-msghdr-on-op_connect-op_bind-asyn.patch
 arm64-entry-fix-arm64-specific-rseq-brokenness.patch
 lockd-fix-test-handling-when-not-all-permissions-are.patch
+firmware-exynos-acpm-count-number-of-commands-in-acpm_xfer.patch
+firmware-exynos-acpm-count-acpm_xfer-buffers-with-__counted_by_ptr.patch
+firmware-samsung-acpm-fix-cross-thread-rx-length-corruption.patch
+firmware-samsung-acpm-fix-false-timeouts-and-use-after-free-in-polling.patch
+firmware-samsung-acpm-fix-missing-lkmm-barriers-in-sequence-allocator.patch