usb: musb: fix crash with highmen PIO and usbmon
authorMans Rullgard <mans@mansr.com>
Mon, 16 Mar 2020 21:11:35 +0000 (16:11 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Apr 2020 14:34:34 +0000 (16:34 +0200)
commit 52974d94a206ce428d9d9b6eaa208238024be82a upstream.

When handling a PIO bulk transfer with highmem buffer, a temporary
mapping is assigned to urb->transfer_buffer.  After the transfer is
complete, an invalid address is left behind in this pointer.  This is
not ordinarily a problem since nothing touches that buffer before the
urb is released.  However, when usbmon is active, usbmon_urb_complete()
calls (indirectly) mon_bin_get_data() which does access the transfer
buffer if it is set.  To prevent an invalid memory access here, reset
urb->transfer_buffer to NULL when finished (musb_host_rx()), or do not
set it at all (musb_host_tx()).

Fixes: 8e8a55165469 ("usb: musb: host: Handle highmem in PIO mode")
Signed-off-by: Mans Rullgard <mans@mansr.com>
Cc: stable@vger.kernel.org
Signed-off-by: Bin Liu <b-liu@ti.com>
Link: https://lore.kernel.org/r/20200316211136.2274-8-b-liu@ti.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/musb/musb_host.c

index 802388bb42ba707ee4432456aaaf1718af151d9f..3ec0752e67ac6fd8a58f67c97842cff46fc72d59 100644 (file)
@@ -1494,10 +1494,7 @@ done:
         * We need to map sg if the transfer_buffer is
         * NULL.
         */
-       if (!urb->transfer_buffer)
-               qh->use_sg = true;
-
-       if (qh->use_sg) {
+       if (!urb->transfer_buffer) {
                /* sg_miter_start is already done in musb_ep_program */
                if (!sg_miter_next(&qh->sg_miter)) {
                        dev_err(musb->controller, "error: sg list empty\n");
@@ -1505,9 +1502,8 @@ done:
                        status = -EINVAL;
                        goto done;
                }
-               urb->transfer_buffer = qh->sg_miter.addr;
                length = min_t(u32, length, qh->sg_miter.length);
-               musb_write_fifo(hw_ep, length, urb->transfer_buffer);
+               musb_write_fifo(hw_ep, length, qh->sg_miter.addr);
                qh->sg_miter.consumed = length;
                sg_miter_stop(&qh->sg_miter);
        } else {
@@ -1516,11 +1512,6 @@ done:
 
        qh->segsize = length;
 
-       if (qh->use_sg) {
-               if (offset + length >= urb->transfer_buffer_length)
-                       qh->use_sg = false;
-       }
-
        musb_ep_select(mbase, epnum);
        musb_writew(epio, MUSB_TXCSR,
                        MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
@@ -2038,8 +2029,10 @@ finish:
        urb->actual_length += xfer_len;
        qh->offset += xfer_len;
        if (done) {
-               if (qh->use_sg)
+               if (qh->use_sg) {
                        qh->use_sg = false;
+                       urb->transfer_buffer = NULL;
+               }
 
                if (urb->status == -EINPROGRESS)
                        urb->status = status;