FAILED: patch "[PATCH] firmware: samsung: acpm: Fix cross-thread RX length" failed to apply to 7.0-stable tree
[email protected]
maintainerThe patch below does not apply to the 7.0-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.0.y
git checkout FETCH_HEAD
git cherry-pick -x f133bd4b5daf71bccdde0ad1a4f47fac76a6bfb1
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026061524-schematic-trapeze-18dc@gregkh' --subject-prefix 'PATCH 7.0.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From f133bd4b5daf71bccdde0ad1a4f47fac76a6bfb1 Mon Sep 17 00:00:00 2001
From: Tudor Ambarus <[email protected]>
Date: Tue, 5 May 2026 13:12:58 +0000
Subject: [PATCH] firmware: samsung: acpm: Fix cross-thread RX length
corruption
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.
drivers/firmware/samsung/exynos-acpm-dvfs.c +3 -0
@@ -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 ; } }
drivers/firmware/samsung/exynos-acpm.c +8 -7
@@ -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);
Sasha Levin
maintainerPATCH 1/3 firmware: exynos-acpm: Count number of commands in acpm_xfer
From: Krzysztof Kozlowski <[email protected]>
[ 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.
Stable-dep-of: f133bd4b5daf ("firmware: samsung: acpm: Fix cross-thread RX length corruption")
--- 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(-)
drivers/firmware/samsung/exynos-acpm-dvfs.c +5 -4
@@ -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 acpm_xfer *xfer, u32 *cmd, size_t cmdlen, { 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_handle *handle,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 acpm_handle *handle,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)
drivers/firmware/samsung/exynos-acpm-pmic.c +7 -7
@@ -63,8 +63,8 @@ static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, { 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_handle *handle,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_handle *handle,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_handle *handle,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_handle *handle,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_handle *handle,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)
drivers/firmware/samsung/exynos-acpm.c +7 -7
@@ -205,7 +205,7 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, 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 *achan, const struct acpm_xfer *xfer)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 *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->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 *handle, const struct acpm_xfer *xfer) 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 *handle, const struct acpm_xfer *xfer)/* 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);
drivers/firmware/samsung/exynos-acpm.h +2 -2
@@ -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; };
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 firmware: exynos-acpm: Count acpm_xfer buffers with __counted_by_ptr
From: Krzysztof Kozlowski <[email protected]>
[ 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.
Stable-dep-of: f133bd4b5daf ("firmware: samsung: acpm: Fix cross-thread RX length corruption")
--- drivers/firmware/samsung/exynos-acpm-dvfs.c | 4 ++-- drivers/firmware/samsung/exynos-acpm.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
drivers/firmware/samsung/exynos-acpm-dvfs.c +2 -2
@@ -25,12 +25,12 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, 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; } }
drivers/firmware/samsung/exynos-acpm.h +2 -2
@@ -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;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 firmware: samsung: acpm: Fix cross-thread RX length corruption
In reply to Sasha LevinFrom: Tudor Ambarus <[email protected]>
[ 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.
--- drivers/firmware/samsung/exynos-acpm-dvfs.c | 3 +++ drivers/firmware/samsung/exynos-acpm.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-)
drivers/firmware/samsung/exynos-acpm-dvfs.c +3 -0
@@ -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 ; } }
drivers/firmware/samsung/exynos-acpm.c +8 -7
@@ -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);
Signature · 1 line
2.53.0