<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18576">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_confbridge: Fix bridge shutdown race condition.<br><br>A race condition exists where if a bridge is left vacant<br>for a split instant, the bridge will shut down and go<br>away at the same instant that somebody else could be trying<br>to join it. The newcomer will find the conference still<br>in conference_bridges and thus take it and run with it.<br>At the same time, shutdown of the bridge completes and it<br>is removed from conference_bridges.<br><br>As a result, the newcomer will end up joining the bridge<br>corresponding to the original conference that is now<br>defunct. When the next party joins the same named bridge,<br>it won't be found in conference_bridges and thus a new<br>bridge gets created. This can happen right afterwards,<br>but in theory it could happen at any point after the first.<br><br>As a result, the newcomer that joined the bridge during<br>the shutdown ends up stranded in a bridge that isn't<br>real and will never get conferenced with anything, since<br>it's been removed from the list of conferences already.<br><br>To prevent this, we now explicitly check during shutdown<br>and abort if the conference status is now empty. Additionally,<br>we set a flag to prevent newcomers from joining the old<br>bridge. On the join side, we ignore bridges that are shutting<br>down to ensure we don't join a dead bridge, and, importantly,<br>do not release the lock between the time the conference is<br>found and the conference state changes, to ensure that<br>an imminent shutdown will abort afterwards.<br><br>ASTERISK-30081 #close<br><br>Change-Id: I2a704dd7dd449bf008a1ee90768ddf95c28bae0d<br>---<br>M apps/app_confbridge.c<br>M apps/confbridge/include/confbridge.h<br>2 files changed, 34 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/76/18576/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c</span><br><span>index ae17bca..f78cc3f 100644</span><br><span>--- a/apps/app_confbridge.c</span><br><span>+++ b/apps/app_confbridge.c</span><br><span>@@ -1524,6 +1524,16 @@</span><br><span> {</span><br><span>    struct pbx_find_info q = { .stacklen = 0 };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_lock(conference);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (conference->state == CONF_STATE_EMPTY) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_debug(1, "Somebody joined the conference again before we could shut down, aborting shutdown\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_unlock(conference);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* We are committed to shutting the bridge down for good. */</span><br><span style="color: hsl(120, 100%, 40%);">+  conference->shuttingdown = 1; /* Ensure nobody else tries to join, cause that's a dead end. */</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(conference);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    /* Called with a reference to conference */</span><br><span>  ao2_unlink(conference_bridges, conference);</span><br><span>  send_conf_end_event(conference);</span><br><span>@@ -1739,6 +1749,25 @@</span><br><span> </span><br><span>        /* Attempt to find an existing conference bridge */</span><br><span>  conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* If we found a bridge, ensure it isn't about to shut down. */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (conference) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_lock(conference);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (conference->shuttingdown) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* This is a dead end. Start fresh. */</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_debug(1, "Conference is about to shut down, we'll make a new one\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                       ao2_unlock(conference);</span><br><span style="color: hsl(120, 100%, 40%);">+                       conference = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * The above prevents us from entering a bridge that is shutting down, if the shutdown started first.</span><br><span style="color: hsl(120, 100%, 40%);">+          * However, if we get here before the shutdown flag is set, we need to ensure the shutdown doesn't proceed.</span><br><span style="color: hsl(120, 100%, 40%);">+                * That can only happen if when shutdown tries to proceed, it sees the bridge is active again.</span><br><span style="color: hsl(120, 100%, 40%);">+                 * Accordingly, do NOT release the conference lock until the state gets updated.</span><br><span style="color: hsl(120, 100%, 40%);">+               */</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (conference && conference->b_profile.max_members) {</span><br><span>            max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1;</span><br><span>  }</span><br><span>@@ -1864,7 +1893,11 @@</span><br><span>                   }</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+         ao2_lock(conference);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>              ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_debug(1, "Reusing conference '%s'.\n", conference_name);</span><br><span>       }</span><br><span> </span><br><span>        ao2_unlock(conference_bridges);</span><br><span>@@ -1872,8 +1905,6 @@</span><br><span>      /* Setup conference bridge user parameters */</span><br><span>        user->conference = conference;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ao2_lock(conference);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        /* Determine if the new user should join the conference muted. */</span><br><span>    if (ast_test_flag(&user->u_profile, USER_OPT_STARTMUTED)</span><br><span>              || (!ast_test_flag(&user->u_profile, USER_OPT_ADMIN) && conference->muted)) {</span><br><span>diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h</span><br><span>index f90f185..6c0a538 100644</span><br><span>--- a/apps/confbridge/include/confbridge.h</span><br><span>+++ b/apps/confbridge/include/confbridge.h</span><br><span>@@ -252,6 +252,7 @@</span><br><span>   unsigned int waitingusers;                                        /*!< Number of waiting users present */</span><br><span>         unsigned int locked:1;                                            /*!< Is this conference bridge locked? */</span><br><span>       unsigned int muted:1;                                             /*!< Is this conference bridge muted? */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int shuttingdown:1;                                      /*!< Is this conference committed to shutting down? */</span><br><span>  struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */</span><br><span>    struct ast_channel *record_chan;                                  /*!< Channel used for recording the conference */</span><br><span>       struct ast_str *record_filename;                                  /*!< Recording filename. */</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/18576">change 18576</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/18576"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I2a704dd7dd449bf008a1ee90768ddf95c28bae0d </div>
<div style="display:none"> Gerrit-Change-Number: 18576 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>