Commit 52974d94 authored by Mans Rullgard's avatar Mans Rullgard Committed by Greg Kroah-Hartman

usb: musb: fix crash with highmen PIO and usbmon

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: 8e8a5516 ("usb: musb: host: Handle highmem in PIO mode")
Signed-off-by: default avatarMans Rullgard <mans@mansr.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarBin Liu <b-liu@ti.com>
Link: https://lore.kernel.org/r/20200316211136.2274-8-b-liu@ti.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e72838d4
...@@ -1436,10 +1436,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) ...@@ -1436,10 +1436,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
* We need to map sg if the transfer_buffer is * We need to map sg if the transfer_buffer is
* NULL. * NULL.
*/ */
if (!urb->transfer_buffer) if (!urb->transfer_buffer) {
qh->use_sg = true;
if (qh->use_sg) {
/* sg_miter_start is already done in musb_ep_program */ /* sg_miter_start is already done in musb_ep_program */
if (!sg_miter_next(&qh->sg_miter)) { if (!sg_miter_next(&qh->sg_miter)) {
dev_err(musb->controller, "error: sg list empty\n"); dev_err(musb->controller, "error: sg list empty\n");
...@@ -1447,9 +1444,8 @@ void musb_host_tx(struct musb *musb, u8 epnum) ...@@ -1447,9 +1444,8 @@ void musb_host_tx(struct musb *musb, u8 epnum)
status = -EINVAL; status = -EINVAL;
goto done; goto done;
} }
urb->transfer_buffer = qh->sg_miter.addr;
length = min_t(u32, length, qh->sg_miter.length); 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; qh->sg_miter.consumed = length;
sg_miter_stop(&qh->sg_miter); sg_miter_stop(&qh->sg_miter);
} else { } else {
...@@ -1458,11 +1454,6 @@ void musb_host_tx(struct musb *musb, u8 epnum) ...@@ -1458,11 +1454,6 @@ void musb_host_tx(struct musb *musb, u8 epnum)
qh->segsize = length; qh->segsize = length;
if (qh->use_sg) {
if (offset + length >= urb->transfer_buffer_length)
qh->use_sg = false;
}
musb_ep_select(mbase, epnum); musb_ep_select(mbase, epnum);
musb_writew(epio, MUSB_TXCSR, musb_writew(epio, MUSB_TXCSR,
MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
...@@ -1977,8 +1968,10 @@ void musb_host_rx(struct musb *musb, u8 epnum) ...@@ -1977,8 +1968,10 @@ void musb_host_rx(struct musb *musb, u8 epnum)
urb->actual_length += xfer_len; urb->actual_length += xfer_len;
qh->offset += xfer_len; qh->offset += xfer_len;
if (done) { if (done) {
if (qh->use_sg) if (qh->use_sg) {
qh->use_sg = false; qh->use_sg = false;
urb->transfer_buffer = NULL;
}
if (urb->status == -EINPROGRESS) if (urb->status == -EINPROGRESS)
urb->status = status; urb->status = status;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment