[PATCH] spi: tegra20-slink: bound copies to t->len
From: Weibin Liu
Date: Wed Oct 14 2026 - 10:19:23 EST
tegra_slink_calculate_curr_xfer_param() measures transfers in words of
DIV_ROUND_UP(bits_per_word, 8) bytes, while the SPI core only
guarantees t->len to be a multiple of
roundup_pow_of_two(DIV_ROUND_UP(bits_per_word, 8)). For word sizes
that are not a power of two (e.g. bits_per_word = 24, a 3-byte word)
the two differ, and the roundup in the non-packed path makes the
driver copy curr_dma_words * bytes_per_word bytes even when t->len is
not a multiple of bytes_per_word. A transfer submitted through the
spidev SPI_IOC_MESSAGE ioctl with bits_per_word = 24 and len = 8 then
copies 9 bytes, up to bytes_per_word-1 bytes past the end of the
core-allocated rx_buf, and reads past tx_buf.
Clamp the number of bytes consumed from tx_buf and produced into
rx_buf to the remaining transfer length in the FIFO and DMA copy
paths, so no access exceeds t->len.
Verified by arithmetic against spi_bpw_to_bytes(): with
bits_per_word = 24 the core accepts len = 8 and the driver's word
arithmetic yields 3 words of 3 bytes, 1 byte past t->len. No Tegra
SLINK hardware was available.
Reviewed-by: Guanglei Zhu <zhugl3@xxxxxxxxxxxx>
Fixes: dc4dc3605639 ("spi: tegra: add spi driver for SLINK controller")
Cc: stable@xxxxxxxxxxxxxxx # 3.8+
Signed-off-by: Weibin Liu <liuwb@xxxxxxxxxxxx>
---
drivers/spi/spi-tegra20-slink.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index ed1fcf57dd30..2d4568bbb959 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -315,6 +315,8 @@ static unsigned tegra_slink_fill_tx_fifo_from_client_txbuf(
max_n_32bit = min(tspi->curr_dma_words, tx_empty_count);
written_words = max_n_32bit;
nbytes = written_words * tspi->bytes_per_word;
+ if (nbytes > t->len - tspi->cur_tx_pos)
+ nbytes = t->len - tspi->cur_tx_pos;
for (count = 0; count < max_n_32bit; count++) {
u32 x = 0;
for (i = 0; nbytes && (i < tspi->bytes_per_word);
@@ -349,9 +351,12 @@ static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf(
tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
read_words += tspi->curr_dma_words;
} else {
+ unsigned int remain = t->len - tspi->cur_rx_pos;
+
for (count = 0; count < rx_full_count; count++) {
u32 x = tegra_slink_readl(tspi, SLINK_RX_FIFO);
- for (i = 0; (i < tspi->bytes_per_word); i++)
+ for (i = 0; (i < tspi->bytes_per_word) && remain;
+ i++, remain--)
*rx_buf++ = (x >> (i*8)) & 0xFF;
}
tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word;
@@ -375,6 +380,10 @@ static void tegra_slink_copy_client_txbuf_to_spi_txbuf(
unsigned int count;
u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos;
unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word;
+ unsigned int remain = t->len - tspi->cur_tx_pos;
+
+ if (consume > remain)
+ consume = remain;
for (count = 0; count < tspi->curr_dma_words; count++) {
u32 x = 0;
@@ -408,10 +417,12 @@ static void tegra_slink_copy_spi_rxbuf_to_client_rxbuf(
unsigned int count;
unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos;
u32 rx_mask = ((u32)1 << t->bits_per_word) - 1;
+ unsigned int remain = t->len - tspi->cur_rx_pos;
for (count = 0; count < tspi->curr_dma_words; count++) {
u32 x = tspi->rx_dma_buf[count] & rx_mask;
- for (i = 0; (i < tspi->bytes_per_word); i++)
+ for (i = 0; (i < tspi->bytes_per_word) && remain;
+ i++, remain--)
*rx_buf++ = (x >> (i*8)) & 0xFF;
}
}
---
base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907
change-id: 20261014-spi-tegra20-slink-copylen-46e6ca26f937
Best regards,
--
Weibin Liu <liuwb@xxxxxxxxxxxx>