Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 1 Aug 2019 19:45:34 +0000 (UTC)
From:      Michael Tuexen <tuexen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r350520 - head/sys/netinet
Message-ID:  <201908011945.x71JjYT4031463@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201908011945.x71JjYT4031463>