]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Fix potential reentrancy problems in chan_sip.
authorRichard Mudgett <rmudgett@digium.com>
Mon, 24 Sep 2012 20:44:27 +0000 (20:44 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Mon, 24 Sep 2012 20:44:27 +0000 (20:44 +0000)
Asterisk v1.8 and later was not as vulnerable to this issue.

* Made find_call() lock each private as it processes the found dialogs.
(Primary cause of ABE-2876)

* Made the other functions that traverse the dialogs container lock each
private as it examines them.

* Fix race condition in sip_call() if the thread that sent the INVITE is
held up long enough for a response to be processed.  The p->initid for the
INVITE retransmission could be added after it was canceled by the response
processing.

* Made __sip_destroy() clean up resource pointers after freeing.  This is
primarily defensive in case someone has a stale private pointer.

* Removed redundant memset() in reqprep().  The call to init_req() already
does the memset() and is the first reference to req in reqprep().

* Removed useless set of req.method in transmit_invite().  The calls to
initreqprep() and reqprep() have to do this because they memset() the req.

JIRA ABE-2876

..........

Merged -r373423 from https://origsvn.digium.com/svn/asterisk/be/branches/C.3-bier
........

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

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

channels/chan_sip.c

index d857b5208ec16d6c9091eaee367e456aab8c3dcd..12b747605f2b6280c203100b6c129b04c905a256 100644 (file)
@@ -5779,9 +5779,10 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
                }
 
                xmitres = transmit_invite(p, SIP_INVITE, 1, 2, uri);
-               sip_pvt_unlock(p);
-               if (xmitres == XMIT_ERROR)
+               if (xmitres == XMIT_ERROR) {
+                       sip_pvt_unlock(p);
                        return -1;
+               }
                p->invitestate = INV_CALLING;
 
                /* Initialize auto-congest time */
@@ -5789,6 +5790,7 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
                                                                dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"),
                                                                dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"),
                                                                dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") );
+               sip_pvt_unlock(p);
        }
        return res;
 }
@@ -5882,6 +5884,7 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
        
        if (p->mwi) {
                p->mwi->call = NULL;
+               p->mwi = NULL;
        }
 
        if (dumphistory)
@@ -5892,29 +5895,37 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
                        ao2_ref(p->options->outboundproxy, -1);
                }
                ast_free(p->options);
+               p->options = NULL;
        }
 
        if (p->notify) {
                ast_variables_destroy(p->notify->headers);
                ast_free(p->notify->content);
                ast_free(p->notify);
+               p->notify = NULL;
        }
        if (p->rtp) {
                ast_rtp_instance_destroy(p->rtp);
+               p->rtp = NULL;
        }
        if (p->vrtp) {
                ast_rtp_instance_destroy(p->vrtp);
+               p->vrtp = NULL;
        }
        if (p->trtp) {
                ast_rtp_instance_destroy(p->trtp);
+               p->trtp = NULL;
        }
-       if (p->udptl)
+       if (p->udptl) {
                ast_udptl_destroy(p->udptl);
+               p->udptl = NULL;
+       }
        if (p->refer) {
                if (p->refer->refer_call) {
                        p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
                }
                ast_free(p->refer);
+               p->refer = NULL;
        }
        if (p->route) {
                free_old_route(p->route);
@@ -5967,6 +5978,7 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
        ast_string_field_free_memory(p);
 
        ast_cc_config_params_destroy(p->cc_params);
+       p->cc_params = NULL;
 
        if (p->epa_entry) {
                ao2_ref(p->epa_entry, -1);
@@ -8197,10 +8209,25 @@ static enum match_req_res match_req_to_dialog(struct sip_pvt *sip_pvt_ptr, struc
 static void forked_invite_init(struct sip_request *req, const char *new_theirtag, struct sip_pvt *original, struct ast_sockaddr *addr)
 {
        struct sip_pvt *p;
+       const char *callid;
+
+       sip_pvt_lock(original);
+       callid = ast_strdupa(original->callid);
+       sip_pvt_unlock(original);
 
-       if (!(p = sip_alloc(original->callid, addr, 1, SIP_INVITE, req)))  {
+       if (!(p = sip_alloc(callid, addr, 1, SIP_INVITE, req)))  {
                return; /* alloc error */
        }
+
+       /* Lock p and original private structures. */
+       sip_pvt_lock(p);
+       while (sip_pvt_trylock(original)) {
+               /* Can't use DEADLOCK_AVOIDANCE since p is an ao2 object */
+               sip_pvt_unlock(p);
+               sched_yield();
+               sip_pvt_lock(p);
+       }
+
        p->invitestate = INV_TERMINATED;
        p->ocseq = original->ocseq;
        p->branch = original->branch;
@@ -8212,6 +8239,9 @@ static void forked_invite_init(struct sip_request *req, const char *new_theirtag
        ast_string_field_set(p, uri, original->uri);
        ast_string_field_set(p, our_contact, original->our_contact);
        ast_string_field_set(p, fullcontact, original->fullcontact);
+
+       sip_pvt_unlock(original);
+
        parse_ok_contact(p, req);
        build_route(p, req, 1, 0);
 
@@ -8219,6 +8249,7 @@ static void forked_invite_init(struct sip_request *req, const char *new_theirtag
        transmit_request(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
 
        pvt_set_needdestroy(p, "forked request"); /* this dialog will terminate once the BYE is responed to or times out. */
+       sip_pvt_unlock(p);
        dialog_unref(p, "setup forked invite termination");
 }
 
@@ -8398,7 +8429,9 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
 
                /* Iterate a list of dialogs already matched by Call-id */
                while (iterator && (sip_pvt_ptr = ao2_iterator_next(iterator))) {
+                       sip_pvt_lock(sip_pvt_ptr);
                        found = match_req_to_dialog(sip_pvt_ptr, &args);
+                       sip_pvt_unlock(sip_pvt_ptr);
 
                        switch (found) {
                        case SIP_REQ_MATCH:
@@ -8422,6 +8455,7 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a
                        case SIP_REQ_NOT_MATCH:
                        default:
                                dialog_unref(sip_pvt_ptr, "pvt did not match incoming SIP msg, unref from search");
+                               break;
                        }
                }
                if (iterator) {
@@ -10685,8 +10719,6 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, ui
        int is_strict = FALSE;          /*!< Strict routing flag */
        int is_outbound = ast_test_flag(&p->flags[0], SIP_OUTGOING);    /* Session direction */
 
-       memset(req, 0, sizeof(struct sip_request));
-
        snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
 
        if (!seqno) {
@@ -12189,19 +12221,21 @@ static void copy_request(struct sip_request *dst, const struct sip_request *src)
 
        /* copy the entire request then restore the original data and content
         * members from the dst request */
-       memcpy(dst, src, sizeof(*dst));
+       *dst = *src;
        dst->data = duplicate;
        dst->content = duplicate_content;
 
        /* copy the data into the dst request */
-       if (!dst->data && !(dst->data = ast_str_create(ast_str_strlen(src->data) + 1)))
+       if (!dst->data && !(dst->data = ast_str_create(ast_str_strlen(src->data) + 1))) {
                return;
+       }
        ast_str_copy_string(&dst->data, src->data);
 
        /* copy the content into the dst request (if it exists) */
        if (src->content) {
-               if (!dst->content && !(dst->content = ast_str_create(ast_str_strlen(src->content) + 1)))
+               if (!dst->content && !(dst->content = ast_str_create(ast_str_strlen(src->content) + 1))) {
                        return;
+               }
                ast_str_copy_string(&dst->content, src->content);
        }
 }
@@ -12674,8 +12708,7 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init,
 {
        struct sip_request req;
        struct ast_variable *var;
-       
-       req.method = sipmethod;
+
        if (init) {/* Bump branch even on initial requests */
                p->branch ^= ast_random();
                p->invite_branch = p->branch;
@@ -18697,13 +18730,18 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags)
        char durbuf[10];
        int duration;
        int durh, durm, durs;
-       struct ast_channel *c = cur->owner;
+       struct ast_channel *c;
        struct __show_chan_arg *arg = __arg;
        int fd = arg->fd;
 
+       sip_pvt_lock(cur);
+       c = cur->owner;
 
-       if (cur->subscribed != NONE) /* Subscriptions */
+       if (cur->subscribed != NONE) {
+               /* Subscriptions */
+               sip_pvt_unlock(cur);
                return 0;       /* don't care, we scan all channels */
+       }
 
        if (!cur->rtp) {
                if (sipdebug) {
@@ -18712,10 +18750,12 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags)
                                invitestate2string[cur->invitestate].desc,
                                "-- No RTP active");
                }
+               sip_pvt_unlock(cur);
                return 0;       /* don't care, we scan all channels */
        }
 
        if (ast_rtp_instance_get_stats(cur->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
+               sip_pvt_unlock(cur);
                ast_log(LOG_WARNING, "Could not get RTP stats.\n");
                return 0;
        }
@@ -18746,6 +18786,7 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags)
                stats.txjitter
        );
        arg->numchans++;
+       sip_pvt_unlock(cur);
 
        return 0;       /* don't care, we scan all channels */
 }
@@ -19083,8 +19124,11 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
 {
        struct sip_pvt *cur = __cur;
        struct __show_chan_arg *arg = __arg;
-       const struct ast_sockaddr *dst = sip_real_dst(cur);
-       
+       const struct ast_sockaddr *dst;
+
+       sip_pvt_lock(cur);
+       dst = sip_real_dst(cur);
+
        /* XXX indentation preserved to reduce diff. Will be fixed later */
        if (cur->subscribed == NONE && !arg->subscriptions) {
                /* set if SIP transfer in progress */
@@ -19119,6 +19163,7 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
                        );
                arg->numchans++;
        }
+       sip_pvt_unlock(cur);
        return 0;       /* don't care, we scan all channels */
 }