Merged revisions 114083 via svnmerge from
authorTerry Wilson <twilson@digium.com>
Fri, 11 Apr 2008 22:48:52 +0000 (22:48 +0000)
committerTerry Wilson <twilson@digium.com>
Fri, 11 Apr 2008 22:48:52 +0000 (22:48 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r114083 | twilson | 2008-04-11 17:32:51 -0500 (Fri, 11 Apr 2008) | 7 lines

Several places in the code called find_callno() (which releases the lock on the pvt structure) and then immediately locked the call and did things with it. Unfortunately, the call can disappear between the find_callno and the lock, causing Bad Stuff(tm) to happen.

Added find_callno_locked() function to return the callno withtout unlocking for instances that it is needed.

(issue #12400)
Reported by: ztel

........

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

channels/chan_iax2.c

index 8cfaf4e..d930340 100644 (file)
@@ -1484,7 +1484,7 @@ static int make_trunk(unsigned short callno, int locked)
  *
  * \note Calling this function while holding another pvt lock can cause a deadlock.
  */
-static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd)
+static int __find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int return_locked)
 {
        int res = 0;
        int x;
@@ -1560,12 +1560,23 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
                        ast_mutex_unlock(&iaxsl[x]);
                        return 0;
                }
-               ast_mutex_unlock(&iaxsl[x]);
+               if (!return_locked)
+                       ast_mutex_unlock(&iaxsl[x]);
                res = x;
        }
        return res;
 }
 
+static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd) {
+
+       return __find_callno(callno, dcallno, sin, new, sockfd, 0);
+}
+
+static int find_callno_locked(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd) {
+
+       return __find_callno(callno, dcallno, sin, new, sockfd, 1);
+}
+
 static void iax2_frame_free(struct iax_frame *fr)
 {
        AST_SCHED_DEL(sched, fr->retrans);
@@ -3888,6 +3899,13 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
        ast_mutex_unlock(&iaxsl[callno]);
        tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "IAX2/%s-%d", i->host, i->callno);
        ast_mutex_lock(&iaxsl[callno]);
+       if (!iaxs[callno]) {
+               if (tmp) {
+                       ast_channel_free(tmp);
+               }
+               ast_mutex_unlock(&iaxsl[callno]);
+               return NULL;
+       }
        iax2_ami_channelupdate(i);
        if (!tmp)
                return NULL;
@@ -7589,12 +7607,10 @@ static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, s
                /* Stop if we don't have enough data */
                if (len > packet_len)
                        break;
-               fr->callno = find_callno(callno & ~IAX_FLAG_FULL, 0, sin, NEW_PREVENT, sockfd);
+               fr->callno = find_callno_locked(callno & ~IAX_FLAG_FULL, 0, sin, NEW_PREVENT, sockfd);
                if (!fr->callno)
                        continue;
 
-               ast_mutex_lock(&iaxsl[fr->callno]);
-
                /* If it's a valid call, deliver the contents.  If not, we
                   drop it, since we don't have a scallno to use for an INVAL */
                /* Process as a mini frame */
@@ -7973,7 +7989,9 @@ static int socket_process(struct iax2_thread *thread)
                                        if (call_to_destroy) {
                                                if (iaxdebug)
                                                        ast_debug(1, "Really destroying %d, having been acked on final message\n", call_to_destroy);
+                                               ast_mutex_lock(&iaxsl[call_to_destroy]);
                                                iax2_destroy(call_to_destroy);
+                                               ast_mutex_unlock(&iaxsl[call_to_destroy]);
                                        }
                                }
                                /* Note how much we've received acknowledgement for */
@@ -9389,13 +9407,14 @@ static int iax2_do_register(struct iax2_registry *reg)
 
        if (!reg->callno) {
                ast_debug(1, "Allocate call number\n");
-               reg->callno = find_callno(0, 0, &reg->addr, NEW_FORCE, defaultsockfd);
+               reg->callno = find_callno_locked(0, 0, &reg->addr, NEW_FORCE, defaultsockfd);
                if (reg->callno < 1) {
                        ast_log(LOG_WARNING, "Unable to create call for registration\n");
                        return -1;
                } else
                        ast_debug(1, "Registration created on call %d\n", reg->callno);
                iaxs[reg->callno]->reg = reg;
+               ast_mutex_unlock(&iaxsl[reg->callno]);
        }
        /* Setup the next registration a little early */
        reg->expire = iax2_sched_replace(reg->expire, sched, 
@@ -9439,11 +9458,10 @@ static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const
        memset(&ied, 0, sizeof(ied));
        iax_ie_append_raw(&ied, IAX_IE_PROVISIONING, provdata.buf, provdata.pos);
 
-       callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
+       callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd);
        if (!callno)
                return -1;
 
-       ast_mutex_lock(&iaxsl[callno]);
        if (iaxs[callno]) {
                /* Schedule autodestruct in case they don't ever give us anything back */
                iaxs[callno]->autoid = iax2_sched_replace(iaxs[callno]->autoid, 
@@ -9675,15 +9693,13 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
        if (pds.port)
                sin.sin_port = htons(atoi(pds.port));
 
-       callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
+       callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd);
        if (callno < 1) {
                ast_log(LOG_WARNING, "Unable to create call\n");
                *cause = AST_CAUSE_CONGESTION;
                return NULL;
        }
 
-       ast_mutex_lock(&iaxsl[callno]);
-
        /* If this is a trunk, update it now */
        ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);       
        if (ast_test_flag(&cai, IAX_TRUNK)) {
@@ -11044,13 +11060,12 @@ static int cache_get_callno_locked(const char *data)
        ast_debug(1, "peer: %s, username: %s, password: %s, context: %s\n",
                pds.peer, pds.username, pds.password, pds.context);
 
-       callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
+       callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd);
        if (callno < 1) {
                ast_log(LOG_WARNING, "Unable to create call\n");
                return -1;
        }
 
-       ast_mutex_lock(&iaxsl[callno]);
        ast_string_field_set(iaxs[callno], dproot, data);
        iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH;