Author: tuexen
Date: Sun Sep 22 11:11:01 2019
New Revision: 352594
URL: https://svnweb.freebsd.org/changeset/base/352594

Log:
  Don't hold the info lock when calling sctp_select_a_tag().
  
  This avoids a double lock bug in the NAT colliding state processing
  of SCTP. Thanks to Felix Weinrank for finding and reporting this issue in
  https://github.com/sctplab/usrsctp/issues/374
  He found this bug using fuzz testing.
  
  MFC after:            3 days

Modified:
  head/sys/netinet/sctp_input.c

Modified: head/sys/netinet/sctp_input.c
==============================================================================
--- head/sys/netinet/sctp_input.c       Sun Sep 22 10:59:51 2019        
(r352593)
+++ head/sys/netinet/sctp_input.c       Sun Sep 22 11:11:01 2019        
(r352594)
@@ -703,34 +703,37 @@ static int
 sctp_handle_nat_colliding_state(struct sctp_tcb *stcb)
 {
        /*
-        * return 0 means we want you to proceed with the abort non-zero
-        * means no abort processing
+        * Return 0 means we want you to proceed with the abort non-zero
+        * means no abort processing.
         */
+       uint32_t new_vtag;
        struct sctpasochead *head;
 
        if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) ||
            (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) {
+               new_vtag = sctp_select_a_tag(stcb->sctp_ep, 
stcb->sctp_ep->sctp_lport, stcb->rport, 1);
                atomic_add_int(&stcb->asoc.refcnt, 1);
                SCTP_TCB_UNLOCK(stcb);
                SCTP_INP_INFO_WLOCK();
                SCTP_TCB_LOCK(stcb);
                atomic_subtract_int(&stcb->asoc.refcnt, 1);
+       } else {
+               return (0);
        }
        if (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) {
                /* generate a new vtag and send init */
                LIST_REMOVE(stcb, sctp_asocs);
-               stcb->asoc.my_vtag = sctp_select_a_tag(stcb->sctp_ep, 
stcb->sctp_ep->sctp_lport, stcb->rport, 1);
+               stcb->asoc.my_vtag = new_vtag;
                head = 
&SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, 
SCTP_BASE_INFO(hashasocmark))];
                /*
                 * put it in the bucket in the vtag hash of assoc's for the
                 * system
                 */
                LIST_INSERT_HEAD(head, stcb, sctp_asocs);
-               sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
                SCTP_INP_INFO_WUNLOCK();
+               sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
                return (1);
-       }
-       if (SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED) {
+       } else {
                /*
                 * treat like a case where the cookie expired i.e.: - dump
                 * current cookie. - generate a new vtag. - resend init.
@@ -740,15 +743,15 @@ sctp_handle_nat_colliding_state(struct sctp_tcb *stcb)
                SCTP_SET_STATE(stcb, SCTP_STATE_COOKIE_WAIT);
                sctp_stop_all_cookie_timers(stcb);
                sctp_toss_old_cookies(stcb, &stcb->asoc);
-               stcb->asoc.my_vtag = sctp_select_a_tag(stcb->sctp_ep, 
stcb->sctp_ep->sctp_lport, stcb->rport, 1);
+               stcb->asoc.my_vtag = new_vtag;
                head = 
&SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, 
SCTP_BASE_INFO(hashasocmark))];
                /*
                 * put it in the bucket in the vtag hash of assoc's for the
                 * system
                 */
                LIST_INSERT_HEAD(head, stcb, sctp_asocs);
-               sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
                SCTP_INP_INFO_WUNLOCK();
+               sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
                return (1);
        }
        return (0);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to