This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit f8ea0f3d029636f32a6baa10bf48a989795a292d Author: Justin Hammond <[email protected]> AuthorDate: Sun Aug 16 17:16:51 2026 +0800 drivers/usbhost: Copy an xHCI stand-in buffer in the caller's context. The copy out of a stand-in was done in the completion handler, which runs on a work queue, while the buffer it copies into may belong to a user process whose addresses mean nothing there. Reading a block device directly from a user program faulted. The caller is blocked until the transfer finishes, so the copy belongs there. An asynchronous transfer has no blocked caller to come back to, so a buffer that would need a stand-in is refused for that path. Its callers are class drivers using kernel memory, which do not need one. The refusal is lifted once the completion path can do the copy itself. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <[email protected]> --- drivers/usbhost/usbhost_xhci.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/usbhost/usbhost_xhci.c b/drivers/usbhost/usbhost_xhci.c index b8068948d68..61c4cced59f 100644 --- a/drivers/usbhost/usbhost_xhci.c +++ b/drivers/usbhost/usbhost_xhci.c @@ -3057,6 +3057,8 @@ static FAR uint8_t *xhci_dma_prepare(FAR struct usbhost_xhci_s *priv, if (!reachable || ((uintptr_t)buffer & (line - 1)) != 0 || (buflen & (line - 1)) != 0) { + /* A stand-in is needed; see xhci_dmacapable() for the same test */ + /* The buffer shares a line with something else. Work in a stand-in * that does not. */ @@ -3107,7 +3109,13 @@ static FAR uint8_t *xhci_dma_prepare(FAR struct usbhost_xhci_s *priv, * * Description: * Read back what the controller wrote, and give up any stand-in buffer. - * Called on completion, before whoever is waiting is woken. + * + * This must run in the context of whoever asked for the transfer, not in + * the completion handler. The buffer being copied back into may belong + * to a user process, and its address means nothing in the work queue + * thread that handles the completion event, where the write would fault + * or corrupt another process. The caller is blocked until the transfer + * finishes anyway. * ****************************************************************************/ @@ -3164,10 +3172,6 @@ static void xhci_transfer_complete(FAR struct usbhost_xhci_s *priv, epinfo = priv->devs[slot - 1].epinfo[ep - 1]; DEBUGASSERT(epinfo != NULL); - /* Read back what the controller wrote before anyone looks at it */ - - xhci_dma_finish(epinfo); - flags = spin_lock_irqsave(&priv->spinlock); /* Get transferred length */ @@ -4353,6 +4357,11 @@ static int xhci_ctrl_xfer(FAR struct usbhost_driver_s *drvr, /* And wait for the transfer to complete */ nbytes = xhci_transfer_wait(priv, ep0info); + + /* As for bulk: the copy back belongs in the caller's context */ + + xhci_dma_finish(ep0info); + return nbytes >= 0 ? OK : (int)nbytes; errout_with_iocwait: @@ -4512,6 +4521,13 @@ static ssize_t xhci_transfer(FAR struct usbhost_driver_s *drvr, /* Then wait for the transfer to complete */ nbytes = xhci_transfer_wait(priv, epinfo); + + /* And bring back what it produced, here rather than in the completion, + * because this is the context the caller's buffer belongs to. + */ + + xhci_dma_finish(epinfo); + return nbytes; errout_with_iocwait:
