]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Protect ast_filestream object when on a channel
authorRussell Bryant <russell@russellbryant.com>
Mon, 27 Jan 2014 01:25:23 +0000 (01:25 +0000)
committerRussell Bryant <russell@russellbryant.com>
Mon, 27 Jan 2014 01:25:23 +0000 (01:25 +0000)
The ast_filestream object gets tacked on to a channel via
chan->timingdata.  It's a reference counted object, but the reference
count isn't used when putting it on a channel.  It's theoretically
possible for another thread to interfere with the channel while it's
unlocked and cause the filestream to get destroyed.

Use the astobj2 reference count to make sure that as long as this code
path is holding on the ast_filestream and passing it into the file.c
playback code, that it knows it's valid.

Bug reported by Leif Madsen.

Review: https://reviewboard.asterisk.org/r/3135/
........

Merged revisions 406566 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 406567 from http://svn.asterisk.org/svn/asterisk/branches/11
........

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

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

include/asterisk/channel.h
main/channel.c
main/file.c

index 5b042a96d9938c63534e6b8f1251b09eee87160a..106117de15a20cdf55d91dbd085d5b5008ad95e7 100644 (file)
@@ -929,6 +929,10 @@ enum {
         * publish.
         */
        AST_FLAG_SNAPSHOT_STAGE = (1 << 25),
+       /*!
+        * The data on chan->timingdata is an astobj2 object.
+        */
+       AST_FLAG_TIMINGDATA_IS_AO2_OBJ = (1 << 26),
 };
 
 /*! \brief ast_bridge_config flags */
@@ -2275,6 +2279,7 @@ int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype);
  * \version 1.6.1 changed samples parameter to rate, accomodates new timing methods
  */
 int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data);
+int ast_settimeout_full(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data, unsigned int is_ao2_obj);
 
 /*!
  * \brief Transfer a channel (if supported).
index 567d8c62140d4ca50cf6c42cbf32cde1c7037b7c..a9f0fbb34298cab0a7bc77c363c622cf0dc01d5c 100644 (file)
@@ -3420,6 +3420,11 @@ int ast_waitfordigit(struct ast_channel *c, int ms)
 }
 
 int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data)
+{
+       return ast_settimeout_full(c, rate, func, data, 0);
+}
+
+int ast_settimeout_full(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data, unsigned int is_ao2_obj)
 {
        int res;
        unsigned int real_rate = rate, max_rate;
@@ -3444,9 +3449,20 @@ int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const v
 
        res = ast_timer_set_rate(ast_channel_timer(c), real_rate);
 
+       if (ast_channel_timingdata(c) && ast_test_flag(ast_channel_flags(c), AST_FLAG_TIMINGDATA_IS_AO2_OBJ)) {
+               ao2_ref(ast_channel_timingdata(c), -1);
+       }
+
        ast_channel_timingfunc_set(c, func);
        ast_channel_timingdata_set(c, data);
 
+       if (data && is_ao2_obj) {
+               ao2_ref(data, 1);
+               ast_set_flag(ast_channel_flags(c), AST_FLAG_TIMINGDATA_IS_AO2_OBJ);
+       } else {
+               ast_clear_flag(ast_channel_flags(c), AST_FLAG_TIMINGDATA_IS_AO2_OBJ);
+       }
+
        if (func == NULL && rate == 0 && ast_channel_fdno(c) == AST_TIMING_FD) {
                /* Clearing the timing func and setting the rate to 0
                 * means that we don't want to be reading from the timingfd
@@ -3795,9 +3811,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                /* save a copy of func/data before unlocking the channel */
                                ast_timing_func_t func = ast_channel_timingfunc(chan);
                                void *data = ast_channel_timingdata(chan);
+                               int got_ref = 0;
+                               if (data && ast_test_flag(ast_channel_flags(chan), AST_FLAG_TIMINGDATA_IS_AO2_OBJ)) {
+                                       ao2_ref(data, 1);
+                                       got_ref = 1;
+                               }
                                ast_channel_fdno_set(chan, -1);
                                ast_channel_unlock(chan);
                                func(data);
+                               if (got_ref) {
+                                       ao2_ref(data, -1);
+                               }
                        } else {
                                ast_timer_set_rate(ast_channel_timer(chan), 0);
                                ast_channel_fdno_set(chan, -1);
index ffdbf1821d54c9331f26474893a4eb73b6630440..458f2544627d5ac5f188c056a2de0917e179df3b 100644 (file)
@@ -902,7 +902,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
 
                        rate = (unsigned int) roundf(samp_rate / ((float) whennext));
 
-                       ast_settimeout(s->owner, rate, ast_fsread_audio, s);
+                       ast_settimeout_full(s->owner, rate, ast_fsread_audio, s, 1);
                } else {
                        ast_channel_streamid_set(s->owner, ast_sched_add(ast_channel_sched(s->owner), whennext / (ast_format_rate(&s->fmt->format) / 1000), ast_fsread_audio, s));
                }