From 761e02da5267f1d6a516469b240da2b37f871c88 Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Thu, 19 Jun 2014 16:40:01 -0500 Subject: [PATCH] dahdi: Protect echocan creation/destruction with mutex. This closes a reference and memory leak when multiple CPUs are enabling echocan on a single channel in parallel. The essential problem is that the call to try_module_get() is not serialized. Two separate threads can come into ioctl_echocan() on the same channel, they coordinate via the dahdi_chan.lock to release any current echocan, but then both create a new echocan state, bump the reference on the module, and the last one through will actually attach the new state to the channel. The earlier reference / memory is leaked. I tried to conceive of a way to fix this leak without adding a new lock, but the choices where calling throught the function pointers with dahdi_chan.lock. Otherwise I needed to change the semantics of echocan_create /free which would ripple through the hardware echocan modules. Signed-off-by: Shaun Ruffell Signed-off-by: Russ Meyerriecks --- drivers/dahdi/dahdi-base.c | 6 +++++- include/dahdi/kernel.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index c49f465..0e2ab22 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -1884,6 +1884,7 @@ static void __dahdi_init_chan(struct dahdi_chan *chan) might_sleep(); spin_lock_init(&chan->lock); + mutex_init(&chan->mutex); init_waitqueue_head(&chan->waitq); if (!chan->master) chan->master = chan; @@ -6331,6 +6332,7 @@ ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, if (ecp->tap_length == 0) { /* disable mode, don't need to inspect params */ + mutex_lock(&chan->mutex); spin_lock_irqsave(&chan->lock, flags); ec_state = chan->ec_state; chan->ec_state = NULL; @@ -6341,7 +6343,7 @@ ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, ec_state->ops->echocan_free(chan, ec_state); release_echocan(ec_current); } - + mutex_unlock(&chan->mutex); return 0; } @@ -6358,6 +6360,7 @@ ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, goto exit_with_free; } + mutex_lock(&chan->mutex); /* free any echocan that may be on the channel already */ spin_lock_irqsave(&chan->lock, flags); ec_state = chan->ec_state; @@ -6428,6 +6431,7 @@ ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, } exit_with_free: + mutex_unlock(&chan->mutex); kfree(params); return ret; diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h index aed5286..831ea5f 100644 --- a/include/dahdi/kernel.h +++ b/include/dahdi/kernel.h @@ -425,6 +425,7 @@ struct dahdi_chan { int lastnumbufs; #endif spinlock_t lock; + struct mutex mutex; char name[40]; /* Specified by DAHDI */ /*! \brief DAHDI channel number */