mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Weibin Liu <liuwb@xiaopeng.com>
To: broonie@kernel.org
Cc: jth@kernel.org, linux-spi@vger.kernel.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: [PATCH] spi: ch341: handle transfers without a TX or RX buffer
Date: Tue, 29 Sep 2026 16:11:52 +0800	[thread overview]
Message-ID: <20260929081152.41753-1-liuwb@xiaopeng.com> (raw)

ch341_transfer_one() unconditionally copies from trans->tx_buf into the
TX packet and hands trans->rx_buf to usb_bulk_msg() for the readback.

The SPI core allows half-duplex transfers where either of the buffers
is NULL, so a transfer without TX data crashes the kernel on the memcpy
and a transfer without RX data makes usb_bulk_msg() store the received
data at address 0.

Only copy TX data when the transfer carries a TX buffer and fall back
to the TX packet buffer as a scratch area for the readback when the
transfer has no RX buffer. The packet buffer is 32 bytes, which covers
the maximum readback length of 31 bytes, and it is not used by
anything else while the readback runs.

Fixes: 8846739f52af ("spi: add ch341a usb2spi driver")
Cc: stable@vger.kernel.org # 6.11+
Signed-off-by: Weibin Liu <liuwb@xiaopeng.com>
---
Reviewer notes:

- Both failure modes are reachable from unprivileged userspace through
  spidev: SPI_IOC_MESSAGE accepts transfers with either of the buffers
  set to NULL, and the driver passes them on as-is.
- The readback fallback reuses the 32-byte TX packet buffer, which
  covers the maximum readback length of 31 bytes. It is only used by
  ch341_transfer_one() and ch341_set_cs(), both of which run
  synchronously from the SPI core's transfer handling, so nothing
  touches the buffer while the readback is in flight.
- Pre-fix reproducer: open /dev/spidev0.0 and issue a single
  SPI_IOC_MESSAGE transfer with tx_buf = NULL (memcpy from NULL in
  ch341_transfer_one()) or with rx_buf = NULL (usb_bulk_msg() stores
  the readback at address 0).

Functionally verified in QEMU on x86_64: a CH341a adapter was emulated
with gadgetfs + dummy_hcd (the emulated device echoes every SPI stream
packet's payload back on the bulk IN endpoint). With this patch
applied the probe completes, and tx-only, rx-only and full-duplex
transfers issued through spidev succeed, with the full-duplex readback
matching the sent payload, and without a splat. The emulator and the
test program are local test tooling and are not part of this
submission.

 drivers/spi/spi-ch341.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c
index 6448a44a8..6c6eb4ac7 100644
--- a/drivers/spi/spi-ch341.c
+++ b/drivers/spi/spi-ch341.c
@@ -78,14 +78,21 @@ static int ch341_transfer_one(struct spi_controller *host,
 
 	ch341->tx_buf[0] = CH341A_CMD_SPI_STREAM;
 
-	memcpy(ch341->tx_buf + 1, trans->tx_buf, len - 1);
+	if (trans->tx_buf)
+		memcpy(ch341->tx_buf + 1, trans->tx_buf, len - 1);
 
 	ret = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, len,
 			   NULL, CH341_DEFAULT_TIMEOUT);
 	if (ret)
 		return ret;
 
-	return usb_bulk_msg(ch341->udev, ch341->read_pipe, trans->rx_buf,
+	/*
+	 * Half-duplex transfers can come without a RX buffer; the packet
+	 * buffer is not used by anything else during the synchronous
+	 * transfer, so reuse it as a scratch area for the readback.
+	 */
+	return usb_bulk_msg(ch341->udev, ch341->read_pipe,
+			    trans->rx_buf ? trans->rx_buf : ch341->tx_buf,
 			    len - 1, NULL, CH341_DEFAULT_TIMEOUT);
 }
 

base-commit: 72d3fcf802c45d00b300f25b848a93c3a2bd7c7e
prerequisite-patch-id: e48582f6ffe124b3806593af6e22b74682c8a83f
prerequisite-patch-id: 369f74b9a7ecde56141b13ec671f9200345a3bab
-- 
2.50.1


             reply	other threads:[~2026-09-29  8:11 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-29  8:11 Weibin Liu [this message]
2026-09-29 14:40 ` Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260929081152.41753-1-liuwb@xiaopeng.com \
    --to=liuwb@xiaopeng.com \
    --cc=broonie@kernel.org \
    --cc=jth@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®