Commit df09c563 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Sasha Levin

usb: renesas_usbhs: fix NULL pointer dereference in xfer_work()

[ Upstream commit 4fdef698 ]

This patch fixes an issue that the xfer_work() is possible to cause
NULL pointer dereference if the usb cable is disconnected while data
transfer is running.

In such case, a gadget driver may call usb_ep_disable()) before
xfer_work() is actually called. In this case, the usbhs_pkt_pop()
will call usbhsf_fifo_unselect(), and then usbhs_pipe_to_fifo()
in xfer_work() will return NULL.

Fixes: e73a9891 ("usb: renesas_usbhs: add DMAEngine support")
Cc: <stable@vger.kernel.org> # v3.1+
Signed-off-by: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
parent 68960057
...@@ -831,27 +831,34 @@ static void xfer_work(struct work_struct *work) ...@@ -831,27 +831,34 @@ static void xfer_work(struct work_struct *work)
{ {
struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work); struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); struct usbhs_fifo *fifo;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); struct dma_chan *chan;
struct device *dev = usbhs_priv_to_dev(priv); struct device *dev = usbhs_priv_to_dev(priv);
enum dma_transfer_direction dir; enum dma_transfer_direction dir;
unsigned long flags;
usbhs_lock(priv, flags);
fifo = usbhs_pipe_to_fifo(pipe);
if (!fifo)
goto xfer_work_end;
chan = usbhsf_dma_chan_get(fifo, pkt);
dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual, desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
pkt->trans, dir, pkt->trans, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK); DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) if (!desc)
return; goto xfer_work_end;
desc->callback = usbhsf_dma_complete; desc->callback = usbhsf_dma_complete;
desc->callback_param = pipe; desc->callback_param = pipe;
if (dmaengine_submit(desc) < 0) { if (dmaengine_submit(desc) < 0) {
dev_err(dev, "Failed to submit dma descriptor\n"); dev_err(dev, "Failed to submit dma descriptor\n");
return; goto xfer_work_end;
} }
dev_dbg(dev, " %s %d (%d/ %d)\n", dev_dbg(dev, " %s %d (%d/ %d)\n",
...@@ -862,6 +869,9 @@ static void xfer_work(struct work_struct *work) ...@@ -862,6 +869,9 @@ static void xfer_work(struct work_struct *work)
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
dma_async_issue_pending(chan); dma_async_issue_pending(chan);
usbhs_pipe_enable(pipe); usbhs_pipe_enable(pipe);
xfer_work_end:
usbhs_unlock(priv, flags);
} }
/* /*
......
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