mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] spi: cadence-xspi: two memory-safety fixes in the slave-DMA paths
@ 2026-09-29  8:11 Weibin Liu
  2026-09-29  8:11 ` [PATCH 1/2] spi: cadence-xspi: reject SDMA transfers larger than the requested length Weibin Liu
  2026-09-29  8:11 ` [PATCH 2/2] spi: cadence-xspi: fix stack buffer overflow in the Marvell b0 path Weibin Liu
  0 siblings, 2 replies; 3+ messages in thread
From: Weibin Liu @ 2026-09-29  8:11 UTC (permalink / raw)
  To: broonie; +Cc: pthombar, wsadowski, linux-spi, linux-kernel

While reviewing the slave-DMA handling of the Cadence XSPI driver I
found two ways in which memory the SPI core handed to the driver can be
overrun:

  1/2 both SDMA handlers copy the byte count reported by SDMA_SIZE_REG
      without checking it against the requested transfer length; a
      device reporting more bytes than were programmed makes the
      handlers walk past the end of the transfer buffers

  2/2 the Marvell b0 transfer path points the SDMA buffers at a
      10-byte stack scratch area for transfers without TX data;
      transfers longer than that overflow it in both directions and
      clock out uninitialized stack bytes to the attached device

The two patches are independent of each other and each carries its own
Fixes: tag, so they can be queued separately.

Tested on x86_64: with both patches applied the driver builds, loads
and unloads cleanly; no xSPI controller is available in this
environment to exercise the slave-DMA paths on hardware. Details are
in the notes of the individual patches.

Signed-off-by: Weibin Liu <liuwb@xiaopeng.com>
---
Weibin Liu (2):
  spi: cadence-xspi: reject SDMA transfers larger than the requested
    length
  spi: cadence-xspi: fix stack buffer overflow in the Marvell b0 path

 drivers/spi/spi-cadence-xspi.c | 52 +++++++++++++++++++++++++++-------
 1 file changed, 42 insertions(+), 10 deletions(-)

-- 
2.50.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] spi: cadence-xspi: reject SDMA transfers larger than the requested length
  2026-09-29  8:11 [PATCH 0/2] spi: cadence-xspi: two memory-safety fixes in the slave-DMA paths Weibin Liu
@ 2026-09-29  8:11 ` Weibin Liu
  2026-09-29  8:11 ` [PATCH 2/2] spi: cadence-xspi: fix stack buffer overflow in the Marvell b0 path Weibin Liu
  1 sibling, 0 replies; 3+ messages in thread
From: Weibin Liu @ 2026-09-29  8:11 UTC (permalink / raw)
  To: broonie; +Cc: pthombar, wsadowski, linux-spi, linux-kernel, stable

The XSPI controller reports the size of the slave-DMA transaction
through SDMA_SIZE_REG. Both SDMA handlers take this register at face
value and copy the reported number of bytes between the SDMA FIFO and
the transfer buffers, without checking it against the length of the
transfer that was actually requested.

While cdns_xspi_adjust_mem_op_size() clamps the size of the operations
issued by the SPI core, the device can still report a bigger SDMA size
than what was programmed, which makes the handlers read from or write
to memory beyond the end of the transfer buffers.

Track the number of bytes requested for the current data phase in a new
sdma_xfer_len field, set by cdns_xspi_send_stig_command() and by the
Marvell b0 transfer path, and reject any SDMA size that exceeds it.

Fixes: a16cc8077627 ("spi: cadence: add support for Cadence XSPI controller")
Cc: stable@vger.kernel.org # 5.16+
Signed-off-by: Weibin Liu <liuwb@xiaopeng.com>
---
Reviewer notes:

- The SDMA size is read back from the device on every transfer, so the
  fix treats SDMA_SIZE_REG as untrusted input: without the bound the
  handlers copy the reported number of bytes between the SDMA FIFO and
  the transfer buffers, whichever direction the transfer has.
- sdma_xfer_len is set right before the command is triggered in both
  paths that use slave DMA, cdns_xspi_send_stig_command() and
  cdns_xspi_transfer_one_message_b0(), and both handlers
  (cdns_xspi_sdma_handle() and marvell_xspi_sdma_handle()) enforce the
  same bound.
- On rejection the data phase aborts with -EIO and the interrupts are
  disabled again, mirroring the handling of a failed
  cdns_xspi_is_sdma_ready() wait right next to it.

Tested on x86_64: with this patch applied the driver builds, loads and
unloads cleanly; no xSPI controller is available to exercise the slave
DMA path on hardware.

 drivers/spi/spi-cadence-xspi.c | 36 +++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 39c868a5b..1f1cd4535 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -332,6 +332,7 @@ struct cdns_xspi_dev {
 	int irq;
 	int cur_cs;
 	unsigned int sdmasize;
+	unsigned int sdma_xfer_len;
 
 	struct completion cmd_complete;
 	struct completion auto_cmd_complete;
@@ -346,7 +347,7 @@ struct cdns_xspi_dev {
 	u8 hw_num_banks;
 
 	const struct cdns_xspi_driver_data *driver_data;
-	void (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi);
+	int (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi);
 	void (*set_interrupts_handler)(struct cdns_xspi_dev *cdns_xspi, bool enabled);
 
 	bool xfer_in_progress;
@@ -496,7 +497,7 @@ static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t
 	iowrite8_rep(dst, (const u8 *)buf + offset, len);
 }
 
-static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
+static int cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
 {
 	u32 sdma_size, sdma_trd_info;
 	u8 sdma_dir;
@@ -505,6 +506,13 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
 	sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG);
 	sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info);
 
+	if (sdma_size > cdns_xspi->sdma_xfer_len) {
+		dev_err(cdns_xspi->dev,
+			"SDMA size %u exceeds the requested length %u\n",
+			sdma_size, cdns_xspi->sdma_xfer_len);
+		return -EINVAL;
+	}
+
 	switch (sdma_dir) {
 	case CDNS_XSPI_SDMA_DIR_READ:
 		cdns_xspi_sdma_read(cdns_xspi, sdma_size);
@@ -514,6 +522,8 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
 		cdns_xspi_sdma_write(cdns_xspi, sdma_size);
 		break;
 	}
+
+	return 0;
 }
 
 static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
@@ -559,6 +569,7 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
 
 		cdns_xspi->in_buffer = op->data.buf.in;
 		cdns_xspi->out_buffer = op->data.buf.out;
+		cdns_xspi->sdma_xfer_len = op->data.nbytes;
 
 		cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
 
@@ -567,7 +578,11 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
 			cdns_xspi->set_interrupts_handler(cdns_xspi, false);
 			return -EIO;
 		}
-		cdns_xspi->sdma_handler(cdns_xspi);
+		ret = cdns_xspi->sdma_handler(cdns_xspi);
+		if (ret) {
+			cdns_xspi->set_interrupts_handler(cdns_xspi, false);
+			return ret;
+		}
 	}
 
 	wait_for_completion(&cdns_xspi->cmd_complete);
@@ -950,7 +965,7 @@ static void m_iowriteq(void __iomem *addr, const void *buf, int len)
 	}
 }
 
-static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
+static int marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
 {
 	u32 sdma_size, sdma_trd_info;
 	u8 sdma_dir;
@@ -959,6 +974,13 @@ static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
 	sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG);
 	sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info);
 
+	if (sdma_size > cdns_xspi->sdma_xfer_len) {
+		dev_err(cdns_xspi->dev,
+			"SDMA size %u exceeds the requested length %u\n",
+			sdma_size, cdns_xspi->sdma_xfer_len);
+		return -EINVAL;
+	}
+
 	switch (sdma_dir) {
 	case CDNS_XSPI_SDMA_DIR_READ:
 		m_ioreadq(cdns_xspi->sdmabase,
@@ -970,6 +992,8 @@ static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
 			     cdns_xspi->out_buffer, sdma_size);
 		break;
 	}
+
+	return 0;
 }
 
 static const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
@@ -1131,9 +1155,11 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
 				cdns_xspi_prepare_transfer(cs, 1, current_transfer_len - 1,
 							   cmd_regs);
 				cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+				cdns_xspi->sdma_xfer_len = current_transfer_len - 1;
 				if (!cdns_xspi_is_sdma_ready(cdns_xspi, true))
 					return -EIO;
-				cdns_xspi->sdma_handler(cdns_xspi);
+				if (cdns_xspi->sdma_handler(cdns_xspi))
+					return -EIO;
 				if (!cdns_xspi_is_stig_ready(cdns_xspi, true))
 					return -EIO;
 

base-commit: 72d3fcf802c45d00b300f25b848a93c3a2bd7c7e
-- 
2.50.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 2/2] spi: cadence-xspi: fix stack buffer overflow in the Marvell b0 path
  2026-09-29  8:11 [PATCH 0/2] spi: cadence-xspi: two memory-safety fixes in the slave-DMA paths Weibin Liu
  2026-09-29  8:11 ` [PATCH 1/2] spi: cadence-xspi: reject SDMA transfers larger than the requested length Weibin Liu
@ 2026-09-29  8:11 ` Weibin Liu
  1 sibling, 0 replies; 3+ messages in thread
From: Weibin Liu @ 2026-09-29  8:11 UTC (permalink / raw)
  To: broonie; +Cc: pthombar, wsadowski, linux-spi, linux-kernel, stable

transfer_one_message_b0() falls back to a 10-byte stack buffer for
transfers which carry no TX data and points both in_buffer and
out_buffer into it.

With a transfer longer than 10 bytes the SDMA branch of the loop moves
up to MRVL_XFER_QWORD_COUNT * MRVL_XFER_QWORD_BYTECOUNT (256) bytes
through those pointers and then advances them by the same amount, so
both directions overflow the scratch buffer: a read transaction copies
SDMA data past the end of the buffer, a write transaction sends stack
contents from beyond it to the SPI bus.

Size the scratch buffer for the largest chunk the loop can request,
keep the buffer pointers pinned to it when the message does not carry
the corresponding TX or RX data, and only advance the pointers which
reference real transfer buffers. Also zero-initialize the scratch
buffer so that padding bytes sent for TX-less transfers no longer leak
uninitialized stack contents.

Fixes: 5cb7651f78e1 ("Marvell HW overlay support for Cadence xSPI")
Cc: stable@vger.kernel.org # 6.11+
Signed-off-by: Weibin Liu <liuwb@xiaopeng.com>
---
Reviewer notes:

- transfer_one_message_b0() only runs on controllers with the Marvell
  overlay (marvell,cn10-xspi-nor); the loop chunks every transfer into
  SDMA rounds of up to MRVL_XFER_QWORD_COUNT * MRVL_XFER_QWORD_BYTECOUNT
  (256) bytes.
- The scratch buffer stays on the stack but is now sized for the largest
  chunk a single SDMA round can move; the old u8 data[10] was too small
  for any transfer longer than 10 bytes.
- The pointers are only advanced when they reference the real transfer
  buffers, and the buffer is zero-initialized so that TX-less transfers
  no longer clock out uninitialized stack bytes to the attached device.

Tested on x86_64: with this patch applied the driver builds, loads and
unloads cleanly; no controller with the Marvell overlay is available to
exercise the b0 path on hardware.

 drivers/spi/spi-cadence-xspi.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 1f1cd4535..3687853e4 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -268,6 +268,7 @@
 #define MRVL_XFER_FUNC_START		 BIT(0)
 #define MRVL_XFER_QWORD_COUNT		 32
 #define MRVL_XFER_QWORD_BYTECOUNT	 8
+#define MRVL_XFER_MAX_LEN (MRVL_XFER_QWORD_COUNT * MRVL_XFER_QWORD_BYTECOUNT)
 
 #define MRVL_XSPI_POLL_TIMEOUT_US	1000
 #define MRVL_XSPI_POLL_DELAY_US		10
@@ -1107,7 +1108,7 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
 	struct spi_device *spi = m->spi;
 	struct spi_transfer *t = NULL;
 
-	const unsigned int max_len = MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT;
+	const unsigned int max_len = MRVL_XFER_MAX_LEN;
 	int current_transfer_len;
 	int cs = spi_get_chipselect(spi, 0);
 	int cs_change = 0;
@@ -1130,13 +1131,16 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
 	list_for_each_entry(t, &m->transfers, transfer_list) {
 		u8 *txd = (u8 *) t->tx_buf;
 		u8 *rxd = (u8 *) t->rx_buf;
-		u8 data[10];
+		u8 data[MRVL_XFER_MAX_LEN] = {0};
 		u32 cmd_regs[6];
 
 		if (!txd)
 			txd = data;
 
-		cdns_xspi->in_buffer = txd + 1;
+		if (rxd)
+			cdns_xspi->in_buffer = rxd;
+		else
+			cdns_xspi->in_buffer = data;
 		cdns_xspi->out_buffer = txd + 1;
 
 		while (t->len) {
@@ -1163,8 +1167,10 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
 				if (!cdns_xspi_is_stig_ready(cdns_xspi, true))
 					return -EIO;
 
-				cdns_xspi->in_buffer += current_transfer_len;
-				cdns_xspi->out_buffer += current_transfer_len;
+				if (rxd)
+					cdns_xspi->in_buffer += current_transfer_len;
+				if (t->tx_buf)
+					cdns_xspi->out_buffer += current_transfer_len;
 			}
 
 			if (rxd) {
-- 
2.50.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-29  8:11 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29  8:11 [PATCH 0/2] spi: cadence-xspi: two memory-safety fixes in the slave-DMA paths Weibin Liu
2026-09-29  8:11 ` [PATCH 1/2] spi: cadence-xspi: reject SDMA transfers larger than the requested length Weibin Liu
2026-09-29  8:11 ` [PATCH 2/2] spi: cadence-xspi: fix stack buffer overflow in the Marvell b0 path Weibin Liu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®