bridge_native_rtp: Deadlock during 4-way conference creation

The change contains a slightly adjusted patch that was on the issue
(submitted by kmoore).  A fix was made by adding in a bridge lock
while calling bridge_start/stop from the framehook callback.  Since
the framehook callback is not called from the bridging core the bridge
is not locked, but needs to be before calling bridge_start.

(closes issue ASTERISK-22749)
Reported by: Kinsey Moore
Review: https://reviewboard.asterisk.org/r/3066/
Patches:
     lock_inversion.diff uploaded by kmoore (license 6273)
........

Merged revisions 403767 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@403768 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/97/197/1
Kevin Harwell 12 years ago
parent f425c4a086
commit 84e1790beb

@ -112,7 +112,16 @@ static enum ast_rtp_glue_result native_rtp_bridge_get(struct ast_channel *c0, st
return audio_glue0_res;
}
static int native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
/*!
* \internal
* \brief Start native RTP bridging of two channels
*
* \param bridge The bridge that had native RTP bridging happening on it
* \param target If remote RTP bridging, the channel that is unheld.
*
* \note Bridge must be locked when calling this function.
*/
static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
{
struct ast_bridge_channel *c0 = AST_LIST_FIRST(&bridge->channels);
struct ast_bridge_channel *c1 = AST_LIST_LAST(&bridge->channels);
@ -128,18 +137,12 @@ static int native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel
RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK), ast_format_cap_destroy);
if (c0 == c1) {
return 0;
return;
}
ast_channel_lock_both(c0->chan, c1->chan);
native_type = native_rtp_bridge_get(c0->chan, c1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
if (glue0->get_codec) {
glue0->get_codec(c0->chan, cap0);
}
if (glue1->get_codec) {
glue1->get_codec(c1->chan, cap1);
}
switch (native_type) {
case AST_RTP_GLUE_RESULT_LOCAL:
if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
@ -155,6 +158,12 @@ static int native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel
break;
case AST_RTP_GLUE_RESULT_REMOTE:
if (glue0->get_codec) {
glue0->get_codec(c0->chan, cap0);
}
if (glue1->get_codec) {
glue1->get_codec(c1->chan, cap1);
}
/* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */
if (!target) {
@ -180,7 +189,8 @@ static int native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel
break;
}
return 0;
ast_channel_unlock(c0->chan);
ast_channel_unlock(c1->chan);
}
static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
@ -202,6 +212,7 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel
return;
}
ast_channel_lock_both(c0->chan, c1->chan);
native_type = native_rtp_bridge_get(c0->chan, c1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
switch (native_type) {
@ -241,6 +252,9 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel
ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n",
ast_channel_name(c0->chan), ast_channel_name(c1->chan));
ast_channel_unlock(c0->chan);
ast_channel_unlock(c1->chan);
}
/*! \brief Frame hook that is called to intercept hold/unhold */
@ -252,16 +266,23 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct a
return f;
}
ast_channel_lock(chan);
bridge = ast_channel_get_bridge(chan);
ast_channel_unlock(chan);
if (bridge) {
/* native_rtp_bridge_start/stop are not being called from bridging
core so we need to lock the bridge prior to calling these functions
Unfortunately that means unlocking the channel, but as it
should not be modified this should be okay...hopefully */
ast_channel_unlock(chan);
ast_bridge_lock(bridge);
if (f->subclass.integer == AST_CONTROL_HOLD) {
native_rtp_bridge_stop(bridge, chan);
} else if ((f->subclass.integer == AST_CONTROL_UNHOLD) || (f->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) {
native_rtp_bridge_start(bridge, chan);
}
ast_bridge_unlock(bridge);
ast_channel_lock(chan);
}
return f;
@ -412,7 +433,8 @@ static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_c
return -1;
}
return native_rtp_bridge_start(bridge, NULL);
native_rtp_bridge_start(bridge, NULL);
return 0;
}
static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)

@ -292,14 +292,11 @@ static int chan_pjsip_set_rtp_peer(struct ast_channel *chan,
struct chan_pjsip_pvt *pvt = channel->pvt;
struct ast_sip_session *session = channel->session;
int changed = 0;
struct ast_channel *bridge_peer;
/* Don't try to do any direct media shenanigans on early bridges */
bridge_peer = ast_channel_bridge_peer(chan);
if ((rtp || vrtp || tpeer) && !bridge_peer) {
if ((rtp || vrtp || tpeer) && !ast_channel_is_bridged(chan)) {
return 0;
}
ast_channel_cleanup(bridge_peer);
if (nat_active && session->endpoint->media.direct_media.disable_on_nat) {
return 0;

@ -32667,11 +32667,8 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
struct sip_pvt *p;
int changed = 0;
/* Lock the channel and the private safely. */
ast_channel_lock(chan);
p = ast_channel_tech_pvt(chan);
if (!p) {
ast_channel_unlock(chan);
return -1;
}
sip_pvt_lock(p);
@ -32679,7 +32676,6 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
/* I suppose it could be argued that if this happens it is a bug. */
ast_debug(1, "The private is not owned by channel %s anymore.\n", ast_channel_name(chan));
sip_pvt_unlock(p);
ast_channel_unlock(chan);
return 0;
}
@ -32688,14 +32684,12 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
!ast_channel_is_bridged(chan) &&
!sip_cfg.directrtpsetup) {
sip_pvt_unlock(p);
ast_channel_unlock(chan);
return 0;
}
if (p->alreadygone) {
/* If we're destroyed, don't bother */
sip_pvt_unlock(p);
ast_channel_unlock(chan);
return 0;
}
@ -32704,7 +32698,6 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
*/
if (nat_active && !ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA_NAT)) {
sip_pvt_unlock(p);
ast_channel_unlock(chan);
return 0;
}
@ -32770,7 +32763,6 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
*/
ast_clear_flag(&p->flags[2], SIP_PAGE3_DIRECT_MEDIA_OUTGOING);
sip_pvt_unlock(p);
ast_channel_unlock(chan);
return 0;
}
@ -32791,7 +32783,6 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
/* Reset lastrtprx timer */
p->lastrtprx = p->lastrtptx = time(NULL);
sip_pvt_unlock(p);
ast_channel_unlock(chan);
return 0;
}

@ -4214,6 +4214,8 @@ int ast_channel_is_bridged(const struct ast_channel *chan);
* \note The returned peer channel is the current peer in the
* bridge when called.
*
* \note Absolutely _NO_ channel locks should be held when calling this function.
*
* \retval NULL Channel not in a bridge or the bridge is not two-party.
* \retval non-NULL Reffed peer channel at time of calling.
*/

@ -6869,13 +6869,11 @@ void ast_do_masquerade(struct ast_channel *original)
}
ast_debug(1, "Done Masquerading %s (%d)\n", ast_channel_name(original), ast_channel_state(original));
ast_channel_unlock(original);
if ((bridged = ast_channel_bridge_peer(original))) {
ast_channel_unlock(original);
ast_indicate(bridged, AST_CONTROL_SRCCHANGE);
ast_channel_unref(bridged);
} else {
ast_channel_unlock(original);
}
ast_indicate(original, AST_CONTROL_SRCCHANGE);

Loading…
Cancel
Save