Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fdef698 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Felipe Balbi
Browse files

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



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>
parent 44963d64
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -810,20 +810,27 @@ static void xfer_work(struct work_struct *work)
{
	struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
	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 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);
	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;

	desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
					pkt->trans, dir,
					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
	if (!desc)
		return;
		goto xfer_work_end;

	desc->callback		= usbhsf_dma_complete;
	desc->callback_param	= pipe;
@@ -831,7 +838,7 @@ static void xfer_work(struct work_struct *work)
	pkt->cookie = dmaengine_submit(desc);
	if (pkt->cookie < 0) {
		dev_err(dev, "Failed to submit dma descriptor\n");
		return;
		goto xfer_work_end;
	}

	dev_dbg(dev, "  %s %d (%d/ %d)\n",
@@ -842,6 +849,9 @@ static void xfer_work(struct work_struct *work)
	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
	dma_async_issue_pending(chan);
	usbhs_pipe_enable(pipe);

xfer_work_end:
	usbhs_unlock(priv, flags);
}

/*