]> 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>
Mon, 6 Jun 2022 22:42:11 +0000 (17:42 -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 6d76dc575509ee6b13130ef64c7346e5de5827ce..210c34417a6c27f89cd7807e7137e09abfbf9e47 100644 (file)
@@ -14214,9 +14214,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) {
@@ -14263,8 +14261,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++) {
@@ -14285,35 +14283,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)) {
@@ -14336,8 +14330,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;
 }