Re: [PATCH] dmaengine: pl330: fix double lock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Iago,

On 2017-01-11 14:00, iari@xxxxxx wrote:
From: Iago Abal <mail@xxxxxxxxxxx>

The static bug finder EBA (http://www.iagoabal.eu/eba/) reported the
following double-lock bug:

     Double lock:
     1. spin_lock_irqsave(pch->lock, flags) at pl330_free_chan_resources:2236;
     2. call to function `pl330_release_channel' immediately after;
     3. call to function `dma_pl330_rqcb' in line 1753;
     4. spin_lock_irqsave(pch->lock, flags) at dma_pl330_rqcb:1505.

I have fixed it as suggested by Marek Szyprowski.

First, I have replaced `pch->lock' with `pl330->lock' in functions
`pl330_alloc_chan_resources' and `pl330_free_chan_resources'. This avoids
the double-lock by acquiring a different lock than `dma_pl330_rqcb'.

NOTE that, as a result, `pl330_free_chan_resources' executes
`list_splice_tail_init' on `pch->work_list' under lock `pl330->lock',
whereas in the rest of the code `pch->work_list' is protected by
`pch->lock'. I don't know if this may cause race conditions. Similarly
`pch->cyclic' is written by `pl330_alloc_chan_resources' under
`pl330->lock' but read by `pl330_tx_submit' under `pch->lock'.

At the moment of initialization and releasing resources this is not
a problem.

Second, I have removed locking from `pl330_request_channel' and
`pl330_release_channel' functions. Function `pl330_request_channel' is
only called from `pl330_alloc_chan_resources', so the lock is already
held. Function `pl330_release_channel' is called from
`pl330_free_chan_resources', which already holds the lock, and from
`pl330_del'. Function `pl330_del' is called in an error path of
`pl330_probe' and at the end of `pl330_remove', but I assume that there
cannot be concurrent accesses to the protected data at those points.

Signed-off-by: Iago Abal <mail@xxxxxxxxxxx>

Reviewed-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>

---
  drivers/dma/pl330.c | 19 ++++++-------------
  1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 87fd015..3370e56 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1696,7 +1696,6 @@ static bool _chan_ns(const struct pl330_dmac *pl330, int i)
  static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
  {
  	struct pl330_thread *thrd = NULL;
-	unsigned long flags;
  	int chans, i;
if (pl330->state == DYING)
@@ -1704,8 +1703,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
chans = pl330->pcfg.num_chan; - spin_lock_irqsave(&pl330->lock, flags);
-
  	for (i = 0; i < chans; i++) {
  		thrd = &pl330->channels[i];
  		if ((thrd->free) && (!_manager_ns(thrd) ||
@@ -1723,8 +1720,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330)
  		thrd = NULL;
  	}
- spin_unlock_irqrestore(&pl330->lock, flags);
-
  	return thrd;
  }
@@ -1742,7 +1737,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev)
  static void pl330_release_channel(struct pl330_thread *thrd)
  {
  	struct pl330_dmac *pl330;
-	unsigned long flags;
if (!thrd || thrd->free)
  		return;
@@ -1754,10 +1748,8 @@ static void pl330_release_channel(struct pl330_thread *thrd)
pl330 = thrd->dmac; - spin_lock_irqsave(&pl330->lock, flags);
  	_free_event(thrd, thrd->ev);
  	thrd->free = true;
-	spin_unlock_irqrestore(&pl330->lock, flags);
  }
/* Initialize the structure for PL330 configuration, that can be used
@@ -2117,20 +2109,20 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
  	struct pl330_dmac *pl330 = pch->dmac;
  	unsigned long flags;
- spin_lock_irqsave(&pch->lock, flags);
+	spin_lock_irqsave(&pl330->lock, flags);
dma_cookie_init(chan);
  	pch->cyclic = false;
pch->thread = pl330_request_channel(pl330);
  	if (!pch->thread) {
-		spin_unlock_irqrestore(&pch->lock, flags);
+		spin_unlock_irqrestore(&pl330->lock, flags);
  		return -ENOMEM;
  	}
tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch); - spin_unlock_irqrestore(&pch->lock, flags);
+	spin_unlock_irqrestore(&pl330->lock, flags);
return 1;
  }
@@ -2228,12 +2220,13 @@ static int pl330_pause(struct dma_chan *chan)
  static void pl330_free_chan_resources(struct dma_chan *chan)
  {
  	struct dma_pl330_chan *pch = to_pchan(chan);
+	struct pl330_dmac *pl330 = pch->dmac;
  	unsigned long flags;
tasklet_kill(&pch->task); pm_runtime_get_sync(pch->dmac->ddma.dev);
-	spin_lock_irqsave(&pch->lock, flags);
+	spin_lock_irqsave(&pl330->lock, flags);
pl330_release_channel(pch->thread);
  	pch->thread = NULL;
@@ -2241,7 +2234,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
  	if (pch->cyclic)
  		list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
- spin_unlock_irqrestore(&pch->lock, flags);
+	spin_unlock_irqrestore(&pl330->lock, flags);
  	pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
  	pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
  }

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland

--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux PCI]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux