summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTudor Ambarus <tudor.ambarus@linaro.org>2026-06-16 18:39:36 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-06-27 11:07:38 +0100
commitca187ed2fb761857593ac7b98bc7bc404dbd8113 (patch)
tree919fe96c7c2bf1473c6627585132b355df5e6bd3
parent4d37e3e1fe9888bccca67df10b86ff0fb2840866 (diff)
downloadlinux-stable-ca187ed2fb761857593ac7b98bc7bc404dbd8113.tar.gz
linux-stable-ca187ed2fb761857593ac7b98bc7bc404dbd8113.zip
firmware: samsung: acpm: Fix cross-thread RX length corruption
[ 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>
-rw-r--r--drivers/firmware/samsung/exynos-acpm-dvfs.c3
-rw-r--r--drivers/firmware/samsung/exynos-acpm.c15
2 files changed, 11 insertions, 7 deletions
diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c
index 06bdf62dea1f..fdea7aa24ca0 100644
--- 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 acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
if (response) {
xfer->rxcnt = cmdlen;
xfer->rxd = cmd;
+ } else {
+ xfer->rxcnt = 0;
+ xfer->rxd = NULL;
}
}
diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c
index b06a6fb4db27..9766425a44ab 100644
--- 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 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);