]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Fix potential crash from race condition due to accessing channel data without the...
authorMark Michelson <mmichelson@digium.com>
Fri, 30 Apr 2010 20:08:15 +0000 (20:08 +0000)
committerMark Michelson <mmichelson@digium.com>
Fri, 30 Apr 2010 20:08:15 +0000 (20:08 +0000)
In res_musiconhold.c, there are several places where a channel's
stream's existence is checked prior to calling ast_closestream on it. The issue
here is that in several cases, the channel was not locked while checking the
stream. The result was that if two threads checked the state of the channel's
stream at approximately the same time, then there could be a situation where
both threads attempt to call ast_closestream on the channel's stream. The result
here is that the refcount for the stream would go below 0, resulting in a crash.

I have added proper channel locking to res_musiconhold.c to ensure that
we do not try to check chan->stream without the channel locked. A Digium customer
has been using this patch for several weeks and has not had any crashes since
applying the patch.

ABE-2147

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

res/res_musiconhold.c

index 012b8a6dfd4d2a6aa8f8cbf9838a6e8ab4e32dff..308d5220c7279b1f222a9a1bbcb6bcc0e0a73cdc 100644 (file)
@@ -288,7 +288,15 @@ static int moh_files_generator(struct ast_channel *chan, void *data, int len, in
        state->sample_queue += samples;
 
        while (state->sample_queue > 0) {
+               ast_channel_lock(chan);
                if ((f = moh_files_readframe(chan))) {
+                       /* We need to be sure that we unlock
+                        * the channel prior to calling
+                        * ast_write. Otherwise, the recursive locking
+                        * that occurs can cause deadlocks when using
+                        * indirect channels, like local channels
+                        */
+                       ast_channel_unlock(chan);
                        state->samples += f->samples;
                        state->sample_queue -= f->samples;
                        res = ast_write(chan, f);
@@ -297,8 +305,10 @@ static int moh_files_generator(struct ast_channel *chan, void *data, int len, in
                                ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
                                return -1;
                        }
-               } else
+               } else {
+                       ast_channel_unlock(chan);
                        return -1;      
+               }
        }
        return res;
 }
@@ -1054,12 +1064,14 @@ static void local_ast_moh_stop(struct ast_channel *chan)
        ast_clear_flag(chan, AST_FLAG_MOH);
        ast_deactivate_generator(chan);
 
+       ast_channel_lock(chan);
        if (chan->music_state) {
                if (chan->stream) {
                        ast_closestream(chan->stream);
                        chan->stream = NULL;
                }
        }
+       ast_channel_unlock(chan);
 }
 
 static void moh_class_destructor(void *obj)