]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
chan_iax2: Prevent deadlock due to duplicate autoservice.
authorNaveen Albert <asterisk@phreaknet.org>
Mon, 16 May 2022 13:01:48 +0000 (13:01 +0000)
committerKevin Harwell <kharwell@digium.com>
Tue, 7 Jun 2022 17:06:01 +0000 (12:06 -0500)
If a switch is invoked using chan_iax2, deadlock can result
because the PBX core is autoservicing the channel while chan_iax2
also then attempts to service it while waiting for the result
of the switch. This removes servicing of the channel to prevent
any conflicts.

ASTERISK-30064 #close

Change-Id: Ie92f206d32f9a36924af734ddde652b21106af22

channels/chan_iax2.c

index 598e9d5f6719adb2824940a88a8334e00487f337..2d1beb940bdcaf4e0681a6ee92bbfefd03edc2c2 100644 (file)
@@ -14219,9 +14219,7 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
 {
        struct iax2_dpcache *dp = NULL;
        struct timeval now = ast_tvnow();
-       int x, com[2], timeout, old = 0, outfd, doabort, callno;
-       struct ast_channel *c = NULL;
-       struct ast_frame *f = NULL;
+       int x, com[2], timeout, doabort, callno;
 
        AST_LIST_TRAVERSE_SAFE_BEGIN(&dpcache, dp, cache_list) {
                if (ast_tvcmp(now, dp->expiry) > 0) {
@@ -14268,8 +14266,8 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
 
        /* By here we must have a dp */
        if (dp->flags & CACHE_FLAG_PENDING) {
-               struct timeval start;
-               int ms;
+               int res;
+               struct pollfd pfd;
                /* Okay, here it starts to get nasty.  We need a pipe now to wait
                   for a reply to come back so long as it's pending */
                for (x = 0; x < ARRAY_LEN(dp->waiters); x++) {
@@ -14290,35 +14288,31 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
                timeout = iaxdefaulttimeout * 1000;
                /* Temporarily unlock */
                AST_LIST_UNLOCK(&dpcache);
-               /* Defer any dtmf */
-               if (chan)
-                       old = ast_channel_defer_dtmf(chan);
                doabort = 0;
-               start = ast_tvnow();
-               while ((ms = ast_remaining_ms(start, timeout))) {
-                       c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &ms);
-                       if (outfd > -1)
-                               break;
-                       if (!c)
-                               continue;
-                       if (!(f = ast_read(c))) {
-                               doabort = 1;
-                               break;
-                       }
-                       ast_frfree(f);
-               }
-               if (!ms) {
+
+               /* chan is in autoservice here, so do NOT service it here! */
+               pfd.fd = com[0];
+               pfd.events = POLLIN;
+               pfd.revents = 0;
+               /* Wait for pipe activity... if the channel hangs up, we'll catch it on the way out. */
+               res = ast_poll(&pfd, 1, timeout);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "poll returned < 0: %s\n", strerror(errno));
+                       return NULL;
+               } else if (!pfd.revents) {
                        ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
                }
+
+               if (ast_check_hangup(chan)) {
+                       doabort = 1;
+               }
+
                AST_LIST_LOCK(&dpcache);
                dp->waiters[x] = -1;
                close(com[1]);
                close(com[0]);
                if (doabort) {
-                       /* Don't interpret anything, just abort.  Not sure what th epoint
-                         of undeferring dtmf on a hung up channel is but hey whatever */
-                       if (!old && chan)
-                               ast_channel_undefer_dtmf(chan);
+                       /* Don't interpret anything, just abort. */
                        return NULL;
                }
                if (!(dp->flags & CACHE_FLAG_TIMEOUT)) {
@@ -14341,8 +14335,6 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
                        }
                }
                /* Our caller will obtain the rest */
-               if (!old && chan)
-                       ast_channel_undefer_dtmf(chan);
        }
        return dp;
 }