diff options
author | rmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b> | 2011-02-11 00:29:17 +0000 |
---|---|---|
committer | rmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b> | 2011-02-11 00:29:17 +0000 |
commit | 8058eaa1e77f3aa77d538f5adba06847cf6265cb (patch) | |
tree | 3287afadcaa7e8ea60ace51e1084090dd2dae8a0 | |
parent | cd97ddc2daee31781b9b4612d981abf1eac5dcf4 (diff) |
Reentrancy problem if outgoing call gets different B channel than requested.
The chan_dahdi pri_fixup_principle() routine needs to protect the Asterisk
channel with the channel lock when it changes the technology private
pointer to a new private structure.
* Added lock protection while pri_fixup_principle() moves a call from one
private structure to another.
* Made some pri_fixup_principle() messages more meaningful.
Partial backport from v1.8 -r300714.
git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@307623 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | channels/chan_dahdi.c | 95 |
1 files changed, 90 insertions, 5 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index a1869dafd..e701b3925 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -8996,7 +8996,7 @@ next: } -#ifdef HAVE_PRI +#if defined(HAVE_PRI) static struct dahdi_pvt *pri_find_crv(struct dahdi_pri *pri, int crv) { struct dahdi_pvt *p; @@ -9008,8 +9008,54 @@ static struct dahdi_pvt *pri_find_crv(struct dahdi_pri *pri, int crv) } return NULL; } +#endif /* defined(HAVE_PRI) */ +#if defined(HAVE_PRI) +/*! + * \internal + * \brief Obtain the DAHDI owner channel lock if the owner exists. + * \since 1.8 + * + * \param pri DAHDI PRI control structure. + * \param chanpos Channel position in the span. + * + * \note Assumes the pri->lock is already obtained. + * \note Assumes the pri->pvts[chanpos]->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_lock_owner(struct dahdi_pri *pri, int chanpos) +{ + for (;;) { + if (!pri->pvts[chanpos]->owner) { + /* There is no owner lock to get. */ + break; + } + if (!ast_channel_trylock(pri->pvts[chanpos]->owner)) { + /* We got the lock */ + break; + } + /* We must unlock the PRI to avoid the possibility of a deadlock */ + ast_mutex_unlock(&pri->lock); + DEADLOCK_AVOIDANCE(&pri->pvts[chanpos]->lock); + ast_mutex_lock(&pri->lock); + } +} +#endif /* defined(HAVE_PRI) */ +#if defined(HAVE_PRI) +/*! + * \internal + * \brief Find the private structure for the libpri call. + * + * \param pri Span controller structure. + * \param channel LibPRI encoded channel ID. + * + * \note Assumes the pri->lock is already obtained. + * + * \retval array-index into private pointer array on success. + * \retval -1 on error. + */ static int pri_find_principle(struct dahdi_pri *pri, int channel) { int x; @@ -9037,7 +9083,22 @@ static int pri_find_principle(struct dahdi_pri *pri, int channel) return principle; } +#endif /* defined(HAVE_PRI) */ +#if defined(HAVE_PRI) +/*! + * \internal + * \brief Fixup the private structure associated with the libpri call. + * + * \param pri Span controller structure. + * \param principle Array-index into private array to move call to if not already there. + * \param c LibPRI opaque call pointer to find if need to move call. + * + * \note Assumes the pri->lock is already obtained. + * + * \retval principle on success. + * \retval -1 on error. + */ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *c) { int x; @@ -9061,14 +9122,30 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call * if (principle != x) { struct dahdi_pvt *new = pri->pvts[principle], *old = pri->pvts[x]; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Moving call from channel %d to channel %d\n", + /* Get locks to safely move to the new private structure. */ + ast_mutex_lock(&old->lock); + sig_pri_lock_owner(pri, x); + ast_mutex_lock(&new->lock); + + if (option_verbose > 2) { + ast_verbose(VERBOSE_PREFIX_3 + "Moving call (%s) from channel %d to %d.\n", + old->owner ? old->owner->name : "", old->channel, new->channel); + } if (new->owner) { - ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n", - old->channel, new->channel, new->channel); + ast_log(LOG_WARNING, + "Can't move call (%s) from channel %d to %d. It is already in use.\n", + old->owner ? old->owner->name : "", + old->channel, new->channel); + ast_mutex_unlock(&new->lock); + if (old->owner) { + ast_mutex_unlock(&old->owner->lock); + } + ast_mutex_unlock(&old->lock); return -1; } + /* Fix it all up now */ new->owner = old->owner; old->owner = NULL; @@ -9104,6 +9181,12 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call * /* More stuff to transfer to the new channel. */ new->call_level = old->call_level; old->call_level = DAHDI_CALL_LEVEL_IDLE; + + ast_mutex_unlock(&old->lock); + if (new->owner) { + ast_mutex_unlock(&new->owner->lock); + } + ast_mutex_unlock(&new->lock); } return principle; } @@ -9135,7 +9218,9 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call * ast_log(LOG_WARNING, "Call specified, but not found?\n"); return -1; } +#endif /* defined(HAVE_PRI) */ +#if defined(HAVE_PRI) static void *do_idle_thread(void *vchan) { struct ast_channel *chan = vchan; |