[PATCH v2 2/2] spi: sprd-adi: enforce the exact transfer length

From: Weibin Liu

Date: Thu Oct 15 2026 - 08:48:03 EST


sprd_adi_transfer_one() never looks at the transfer length: the
read path dereferences t->rx_buf as one u32 and stores the result
back, and the write path reads two consecutive u32 values from
t->tx_buf. A transfer whose length does not exactly match what the
controller touches is a bug in the caller: a short transfer reads
or writes past the end of the buffer, and a long transfer is
silently truncated.

The SPI core does not enforce the expected length, so reject reads
whose length is not exactly sizeof(u32) and writes whose length is
not exactly 2 * sizeof(u32) with -EINVAL before touching the
buffers.

The only in-tree user, the SC27xx PMIC driver, is updated by the
previous patch to advertise the byte length of an ADI register
access.

Tested in QEMU (aarch64 virt) against Linux 7.3-rc7, with the
controller node added to the guest device tree. QEMU has no model
of this hardware (the MMIO window is backed by RAM), so this only
exercises the software path. Transfers submitted through spidev
confirm the new checks: a 4-byte read and an 8-byte write are
accepted, every other length from 1 to 16 is rejected with
-EINVAL, and the SC27xx driver's transfers pass once it
advertises the byte length. No Spreadtrum hardware was available.

Fixes: 7e2903cb91df ("spi: Add ADI driver for Spreadtrum platform")
Cc: stable@xxxxxxxxxxxxxxx # 4.15+
Signed-off-by: Weibin Liu <liuwb@xxxxxxxxxxxx>
Reviewed-by: Guanglei Zhu <zhugl3@xxxxxxxxxxxx>
---
drivers/spi/spi-sprd-adi.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c
index a6baaf02b3c8..ecec1da3ca7a 100644
--- a/drivers/spi/spi-sprd-adi.c
+++ b/drivers/spi/spi-sprd-adi.c
@@ -339,10 +339,20 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
int ret;

if (t->rx_buf) {
+ /* ADI reads consume exactly one u32: reg in, value out. */
+ if (t->len != sizeof(u32)) {
+ dev_err(sadi->dev, "invalid read length %u\n", t->len);
+ return -EINVAL;
+ }
reg = *(u32 *)t->rx_buf;
ret = sprd_adi_read(sadi, reg, &val);
*(u32 *)t->rx_buf = val;
} else if (t->tx_buf) {
+ /* ADI writes consume exactly two u32: reg and value. */
+ if (t->len != 2 * sizeof(u32)) {
+ dev_err(sadi->dev, "invalid write length %u\n", t->len);
+ return -EINVAL;
+ }
u32 *p = (u32 *)t->tx_buf;
reg = *p++;
val = *p;

--
2.50.1