[PATCH] spi: pic32-sqi: guard against empty BD list in pic32_sqi_one_message()
From: Li Ximo
Date: Mon Oct 26 2026 - 07:59:30 EST
From: Wang Haoran <haoranwangsec@xxxxxxxxx>
pic32_sqi_one_message() accesses sqi->bd_list_used with list_last_entry()
and list_first_entry() without checking whether the list is empty. These
macros do not return NULL for an empty list; they return the
container_of() of the list head pointer, i.e. a wild pointer into struct
pic32_sqi. The wild pointer is dereferenced immediately (bd_ctrl flag
update) and its bd_dma member is written to the
PESQI_BD_BASE_ADDR_REG MMIO register, corrupting the DMA engine state.
The list becomes empty if a message with zero transfers is ever passed
in: the per-transfer loop is then a no-op, no ring descriptor is moved
into bd_list_used, and the code falls through to the list accesses.
Note that this is not reachable from userspace today, because the SPI
core rejects messages with an empty transfer list in __spi_validate()
(drivers/spi/spi.c) before they reach the controller driver - and the
core's own spi_transfer_one_message() likewise dereferences the first
transfer unconditionally. This change is therefore defensive hardening,
found by code audit; we do not have PIC32 hardware to exercise this
path at runtime.
Fix it by bailing out with -EINVAL when bd_list_used is empty, before
both list_last_entry() and list_first_entry(). The xfer_out cleanup path
tolerates an empty list, and normal messages with at least one transfer
are unaffected. This also matches the existing list_empty() guard in
ring_desc_get() in the same file.
Reported-by: Wang Haoran <haoranwangsec@xxxxxxxxx>
Reported-by: Li Ximo <lixicaicai@xxxxxxxxx>
Signed-off-by: Wang Haoran <haoranwangsec@xxxxxxxxx>
---
drivers/spi/spi-pic32-sqi.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c
index 5d3921e29..005bb6a0c 100644
--- a/drivers/spi/spi-pic32-sqi.c
+++ b/drivers/spi/spi-pic32-sqi.c
@@ -391,6 +391,12 @@ static int pic32_sqi_one_message(struct spi_controller *host,
}
}
+ /* a message with no transfers leaves the BD list empty */
+ if (list_empty(&sqi->bd_list_used)) {
+ ret = -EINVAL;
+ goto xfer_out;
+ }
+
/* BDs are prepared and chained. Now mark LAST_BD, CS_DEASSERT at last
* element of the list.
*/
--
2.30.2