Author: tuexen
Date: Thu Aug  1 19:45:34 2019
New Revision: 350520
URL: https://svnweb.freebsd.org/changeset/base/350520

Log:
  Fix the reporting of multiple unknown parameters in an received INIT
  chunk. This also plugs an potential mbuf leak.
  Thanks to Felix Weinrank for reporting this issue found by fuzz-testing
  the userland stack.
  
  MFC after:            3 days

Modified:
  head/sys/netinet/sctp_output.c

Modified: head/sys/netinet/sctp_output.c
==============================================================================
--- head/sys/netinet/sctp_output.c      Thu Aug  1 19:26:16 2019        
(r350519)
+++ head/sys/netinet/sctp_output.c      Thu Aug  1 19:45:34 2019        
(r350520)
@@ -4988,17 +4988,17 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_
         */
        struct sctp_paramhdr *phdr, params;
 
-       struct mbuf *mat, *op_err;
+       struct mbuf *mat, *m_tmp, *op_err, *op_err_last;
        int at, limit, pad_needed;
        uint16_t ptype, plen, padded_size;
-       int err_at;
 
        *abort_processing = 0;
        mat = in_initpkt;
-       err_at = 0;
        limit = ntohs(cp->chunk_length) - sizeof(struct sctp_init_chunk);
        at = param_offset;
        op_err = NULL;
+       op_err_last = NULL;
+       pad_needed = 0;
        SCTPDBG(SCTP_DEBUG_OUTPUT1, "Check for unrecognized param's\n");
        phdr = sctp_get_next_param(mat, at, &params, sizeof(params));
        while ((phdr != NULL) && ((size_t)limit >= sizeof(struct 
sctp_paramhdr))) {
@@ -5123,6 +5123,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_
                                *abort_processing = 1;
                                sctp_m_freem(op_err);
                                op_err = NULL;
+                               op_err_last = NULL;
 #ifdef INET6
                                l_len = SCTP_MIN_OVERHEAD;
 #else
@@ -5131,7 +5132,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_
                                l_len += sizeof(struct sctp_chunkhdr);
                                l_len += sizeof(struct sctp_gen_error_cause);
                                op_err = sctp_get_mbuf_for_msg(l_len, 0, 
M_NOWAIT, 1, MT_DATA);
-                               if (op_err) {
+                               if (op_err != NULL) {
                                        /*
                                         * Pre-reserve space for IP, SCTP,
                                         * and chunk header.
@@ -5151,6 +5152,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_
                                        if (SCTP_BUF_NEXT(op_err) == NULL) {
                                                sctp_m_freem(op_err);
                                                op_err = NULL;
+                                               op_err_last = NULL;
                                        }
                                }
                                return (op_err);
@@ -5186,37 +5188,55 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_
 #endif
                                                SCTP_BUF_RESV_UF(op_err, 
sizeof(struct sctphdr));
                                                SCTP_BUF_RESV_UF(op_err, 
sizeof(struct sctp_chunkhdr));
+                                               op_err_last = op_err;
                                        }
                                }
-                               if (op_err) {
+                               if (op_err != NULL) {
                                        /* If we have space */
-                                       struct sctp_paramhdr s;
+                                       struct sctp_paramhdr *param;
 
-                                       if (err_at % 4) {
-                                               uint32_t cpthis = 0;
-
-                                               pad_needed = 4 - (err_at % 4);
-                                               m_copyback(op_err, err_at, 
pad_needed, (caddr_t)&cpthis);
-                                               err_at += pad_needed;
+                                       if (pad_needed > 0) {
+                                               op_err_last = 
sctp_add_pad_tombuf(op_err_last, pad_needed);
                                        }
-                                       s.param_type = 
htons(SCTP_UNRECOG_PARAM);
-                                       s.param_length = 
htons((uint16_t)sizeof(struct sctp_paramhdr) + plen);
-                                       m_copyback(op_err, err_at, 
sizeof(struct sctp_paramhdr), (caddr_t)&s);
-                                       err_at += sizeof(struct sctp_paramhdr);
-                                       SCTP_BUF_NEXT(op_err) = 
SCTP_M_COPYM(mat, at, plen, M_NOWAIT);
-                                       if (SCTP_BUF_NEXT(op_err) == NULL) {
+                                       if (op_err_last == NULL) {
                                                sctp_m_freem(op_err);
-                                               /*
-                                                * we are out of memory but
-                                                * we still need to have a
-                                                * look at what to do (the
-                                                * system is in trouble
-                                                * though).
-                                                */
                                                op_err = NULL;
+                                               op_err_last = NULL;
                                                goto more_processing;
                                        }
-                                       err_at += plen;
+                                       if (M_TRAILINGSPACE(op_err_last) < 
(int)sizeof(struct sctp_paramhdr)) {
+                                               m_tmp = 
sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), 0, M_NOWAIT, 1, MT_DATA);
+                                               if (m_tmp == NULL) {
+                                                       sctp_m_freem(op_err);
+                                                       op_err = NULL;
+                                                       op_err_last = NULL;
+                                                       goto more_processing;
+                                               }
+                                               SCTP_BUF_LEN(m_tmp) = 0;
+                                               SCTP_BUF_NEXT(m_tmp) = NULL;
+                                               SCTP_BUF_NEXT(op_err_last) = 
m_tmp;
+                                               op_err_last = m_tmp;
+                                       }
+                                       param = (struct sctp_paramhdr 
*)(mtod(op_err_last, caddr_t)+SCTP_BUF_LEN(op_err_last));
+                                       param->param_type = 
htons(SCTP_UNRECOG_PARAM);
+                                       param->param_length = 
htons((uint16_t)sizeof(struct sctp_paramhdr) + plen);
+                                       SCTP_BUF_LEN(op_err_last) += 
sizeof(struct sctp_paramhdr);
+                                       SCTP_BUF_NEXT(op_err_last) = 
SCTP_M_COPYM(mat, at, plen, M_NOWAIT);
+                                       if (SCTP_BUF_NEXT(op_err_last) == NULL) 
{
+                                               sctp_m_freem(op_err);
+                                               op_err = NULL;
+                                               op_err_last = NULL;
+                                               goto more_processing;
+                                       } else {
+                                               while 
(SCTP_BUF_NEXT(op_err_last) != NULL) {
+                                                       op_err_last = 
SCTP_BUF_NEXT(op_err_last);
+                                               }
+                                       }
+                                       if (plen % 4 != 0) {
+                                               pad_needed = 4 - (plen % 4);
+                                       } else {
+                                               pad_needed = 0;
+                                       }
                                }
                        }
        more_processing:
@@ -5239,6 +5259,7 @@ invalid_size:
        *abort_processing = 1;
        sctp_m_freem(op_err);
        op_err = NULL;
+       op_err_last = NULL;
        if (phdr != NULL) {
                struct sctp_paramhdr *param;
                int l_len;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to