]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: samsung: acpm: Fix cross-thread RX length corruption
authorTudor Ambarus <tudor.ambarus@linaro.org>
Tue, 5 May 2026 13:12:58 +0000 (13:12 +0000)
committerKrzysztof Kozlowski <krzk@kernel.org>
Thu, 14 May 2026 16:54:34 +0000 (18:54 +0200)
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>
drivers/firmware/samsung/exynos-acpm-dvfs.c
drivers/firmware/samsung/exynos-acpm.c

index 06bdf62dea1f30b69b14c5da565955c9f7e97b95..fdea7aa24ca02e886ec252eed5793e64ba071bd6 100644 (file)
@@ -31,6 +31,9 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
        if (response) {
                xfer->rxcnt = cmdlen;
                xfer->rxd = cmd;
+       } else {
+               xfer->rxcnt = 0;
+               xfer->rxd = NULL;
        }
 }
 
index 16c46ed60837165ae86978c83b32985b62dcfd42..e95edc350efa6c5607325dc4d324653a1aa7853f 100644 (file)
@@ -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 acpm_chan *achan,
        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 *achan, const struct acpm_xfer *xfer)
                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 *achan, const struct acpm_xfer *xfer)
                                 * 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 acpm_chan *achan,
        /* 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);