Author: tuexen
Date: Fri Dec 18 10:13:28 2020
New Revision: 368757
URL: https://svnweb.freebsd.org/changeset/base/368757

Log:
  MFC r368593:
  Clean up more resouces of an existing SCTP association in case of
  a restart.
  This fixes a use-after-free scenario, which was reported by Felix
  Wilhelm from Google in case a peer is able to modify the cookie.
  However, this can also be triggered by an assciation restart under
  some specific conditions.
  
  MFC r368622:
  Harden the handling of outgoing streams in case of an restart or INIT
  collision. This avouds an out-of-bounce access in case the peer can
  break the cookie signature. Thanks to Felix Wilhelm from Google for
  reporting the issue.

Modified:
  stable/12/sys/netinet/sctp_input.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/netinet/sctp_input.c
==============================================================================
--- stable/12/sys/netinet/sctp_input.c  Fri Dec 18 10:09:21 2020        
(r368756)
+++ stable/12/sys/netinet/sctp_input.c  Fri Dec 18 10:13:28 2020        
(r368757)
@@ -1433,6 +1433,11 @@ sctp_process_cookie_existing(struct mbuf *m, int iphle
        struct sctp_association *asoc;
        struct sctp_init_chunk *init_cp, init_buf;
        struct sctp_init_ack_chunk *initack_cp, initack_buf;
+       struct sctp_asconf_addr *aparam, *naparam;
+       struct sctp_asconf_ack *aack, *naack;
+       struct sctp_tmit_chunk *chk, *nchk;
+       struct sctp_stream_reset_list *strrst, *nstrrst;
+       struct sctp_queued_to_read *sq, *nsq;
        struct sctp_nets *net;
        struct mbuf *op_err;
        struct timeval old;
@@ -1700,7 +1705,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphle
                            NULL);
                }
                asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
-               asoc->pre_open_streams = 
ntohs(initack_cp->init.num_outbound_streams);
+               if (asoc->pre_open_streams < asoc->streamoutcnt) {
+                       asoc->pre_open_streams = asoc->streamoutcnt;
+               }
 
                if (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) {
                        /*
@@ -1711,7 +1718,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphle
                         * still take a timeout to move these.. but it can't
                         * hurt to mark them.
                         */
-                       struct sctp_tmit_chunk *chk;
 
                        TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
                                if (chk->sent < SCTP_DATAGRAM_RESEND) {
@@ -1834,7 +1840,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphle
                        /* move to OPEN state, if not in SHUTDOWN_SENT */
                        SCTP_SET_STATE(stcb, SCTP_STATE_OPEN);
                }
-               asoc->pre_open_streams = 
ntohs(initack_cp->init.num_outbound_streams);
+               if (asoc->pre_open_streams < asoc->streamoutcnt) {
+                       asoc->pre_open_streams = asoc->streamoutcnt;
+               }
                asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
                asoc->sending_seq = asoc->asconf_seq_out = 
asoc->str_reset_seq_out = asoc->init_seq_number;
                asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
@@ -1875,6 +1883,57 @@ sctp_process_cookie_existing(struct mbuf *m, int iphle
                        stcb->asoc.strmout[i].next_mid_unordered = 0;
                        stcb->asoc.strmout[i].last_msg_incomplete = 0;
                }
+               TAILQ_FOREACH_SAFE(strrst, &asoc->resetHead, next_resp, 
nstrrst) {
+                       TAILQ_REMOVE(&asoc->resetHead, strrst, next_resp);
+                       SCTP_FREE(strrst, SCTP_M_STRESET);
+               }
+               TAILQ_FOREACH_SAFE(sq, &asoc->pending_reply_queue, next, nsq) {
+                       TAILQ_REMOVE(&asoc->pending_reply_queue, sq, next);
+                       if (sq->data) {
+                               sctp_m_freem(sq->data);
+                               sq->data = NULL;
+                       }
+                       sctp_free_remote_addr(sq->whoFrom);
+                       sq->whoFrom = NULL;
+                       sq->stcb = NULL;
+                       sctp_free_a_readq(stcb, sq);
+               }
+               TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, 
nchk) {
+                       TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+                       if (chk->data) {
+                               sctp_m_freem(chk->data);
+                               chk->data = NULL;
+                       }
+                       if (chk->holds_key_ref)
+                               sctp_auth_key_release(stcb, chk->auth_keyid, 
SCTP_SO_LOCKED);
+                       sctp_free_remote_addr(chk->whoTo);
+                       SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
+                       SCTP_DECR_CHK_COUNT();
+               }
+               TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, 
nchk) {
+                       TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next);
+                       if (chk->data) {
+                               sctp_m_freem(chk->data);
+                               chk->data = NULL;
+                       }
+                       if (chk->holds_key_ref)
+                               sctp_auth_key_release(stcb, chk->auth_keyid, 
SCTP_SO_LOCKED);
+                       sctp_free_remote_addr(chk->whoTo);
+                       SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
+                       SCTP_DECR_CHK_COUNT();
+               }
+               TAILQ_FOREACH_SAFE(aparam, &asoc->asconf_queue, next, naparam) {
+                       TAILQ_REMOVE(&asoc->asconf_queue, aparam, next);
+                       SCTP_FREE(aparam, SCTP_M_ASC_ADDR);
+               }
+               TAILQ_FOREACH_SAFE(aack, &asoc->asconf_ack_sent, next, naack) {
+                       TAILQ_REMOVE(&asoc->asconf_ack_sent, aack, next);
+                       if (aack->data != NULL) {
+                               sctp_m_freem(aack->data);
+                       }
+                       SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asconf_ack), 
aack);
+               }
+
                /* process the INIT-ACK info (my info) */
                asoc->my_vtag = ntohl(initack_cp->init.initiate_tag);
                asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
@@ -2061,7 +2120,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, in
        /* process the INIT-ACK info (my info) */
        asoc->my_vtag = ntohl(initack_cp->init.initiate_tag);
        asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
-       asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams);
        asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
        asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = 
asoc->init_seq_number;
        asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
_______________________________________________
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