]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Fix a major bug in autoservice. There was a race condition in the handling of
authorRussell Bryant <russell@russellbryant.com>
Fri, 29 Feb 2008 23:34:32 +0000 (23:34 +0000)
committerRussell Bryant <russell@russellbryant.com>
Fri, 29 Feb 2008 23:34:32 +0000 (23:34 +0000)
the list of channels in autoservice.  The problem was that it was possible for
a channel to get removed from autoservice and destroyed, while the autoservice
thread was still messing with the channel.  This led to memory corruption, and
caused crashes.  This explains multiple backtraces I have seen that have
references to autoservice, but do to the nature of the issue (memory corruption),
could cause crashes in a number of areas.

(fixes the crash in BE-386)
(closes issue #11694)
(closes issue #11940)

The following issues could be related.  If you are the reporter of one of these,
please update to include this fix and try again.

(potentially fixes issue #11189)
(potentially fixes issue #12107)
(potentially fixes issue #11573)
(potentially fixes issue #12008)
(potentially fixes issue #11189)
(potentially fixes issue #11993)
(potentially fixes issue #11791)

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@105409 65c4cc65-6c06-0410-ace0-fbb531ad65f3

main/autoservice.c

index 140103dba0e42a48c878059b46a36f30edd144e2..0a641e97c327f9bb9f826a124c1c84723fbf1495 100644 (file)
@@ -67,6 +67,8 @@ static AST_LIST_HEAD_STATIC(aslist, asent);
 
 static pthread_t asthread = AST_PTHREADT_NULL;
 
+static int as_chan_list_state;
+
 static void defer_frame(struct ast_channel *chan, struct ast_frame *f)
 {
        struct ast_frame *dup_f;
@@ -91,6 +93,11 @@ static void *autoservice_run(void *ign)
                int x = 0, ms = 500;
 
                AST_LIST_LOCK(&aslist);
+
+               /* At this point, we know that no channels that have been removed are going
+                * to get used again. */
+               as_chan_list_state++;
+
                AST_LIST_TRAVERSE(&aslist, as, list) {
                        if (!as->chan->_softhangup) {
                                if (x < MAX_AUTOMONS)
@@ -215,10 +222,18 @@ int ast_autoservice_stop(struct ast_channel *chan)
        struct ast_frame *f;
        int removed = 0;
        int orig_end_dtmf_flag = 0;
+       int chan_list_state;
 
        AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames);
 
        AST_LIST_LOCK(&aslist);
+
+       /* Save the autoservice channel list state.  We _must_ verify that the channel
+        * list has been rebuilt before we return.  Because, after we return, the channel
+        * could get destroyed and we don't want our poor autoservice thread to step on
+        * it after its gone! */
+       chan_list_state = as_chan_list_state;
+
        AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {       
                if (as->chan == chan) {
                        as->use_count--;
@@ -256,5 +271,8 @@ int ast_autoservice_stop(struct ast_channel *chan)
                ast_frfree(f);
        }
 
+       while (chan_list_state == as_chan_list_state)
+               usleep(1000);
+
        return res;
 }