Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 27 Jan 2015 19:37:03 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-releng@freebsd.org
Subject:   svn commit: r277808 - in releng: 10.0 10.0/sys/conf 10.0/sys/netinet 10.1 10.1/sys/conf 10.1/sys/netinet 8.4 8.4/sys/conf 8.4/sys/netinet 9.3 9.3/sys/conf 9.3/sys/netinet
Message-ID:  <201501271937.t0RJb3UI015565@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Tue Jan 27 19:37:02 2015
New Revision: 277808
URL: https://svnweb.freebsd.org/changeset/base/277808

Log:
  Fix SCTP SCTP_SS_VALUE kernel memory corruption and disclosure vulnerability
  and SCTP stream reset vulnerability.
  
  Security:	FreeBSD-SA-15:02.kmem
  Security:	CVE-2014-8612
  Security:	FreeBSD-SA-15:03.sctp
  Security:	CVE-2014-8613
  Approved by:	so

Modified:
  releng/10.0/UPDATING
  releng/10.0/sys/conf/newvers.sh
  releng/10.0/sys/netinet/sctp_input.c
  releng/10.0/sys/netinet/sctp_usrreq.c
  releng/10.1/UPDATING
  releng/10.1/sys/conf/newvers.sh
  releng/10.1/sys/netinet/sctp_input.c
  releng/10.1/sys/netinet/sctp_usrreq.c
  releng/8.4/UPDATING
  releng/8.4/sys/conf/newvers.sh
  releng/8.4/sys/netinet/sctp_input.c
  releng/8.4/sys/netinet/sctp_usrreq.c
  releng/9.3/UPDATING
  releng/9.3/sys/conf/newvers.sh
  releng/9.3/sys/netinet/sctp_input.c
  releng/9.3/sys/netinet/sctp_usrreq.c

Modified: releng/10.0/UPDATING
==============================================================================
--- releng/10.0/UPDATING	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.0/UPDATING	Tue Jan 27 19:37:02 2015	(r277808)
@@ -16,6 +16,14 @@ from older versions of FreeBSD, try WITH
 stable/10, and then rebuild without this option. The bootstrap process from
 older version of current is a bit fragile.
 
+20150127:	p17	FreeBSD-SA-15:02.kmem
+			FreeBSD-SA-15:03.sctp
+
+	Fix SCTP SCTP_SS_VALUE kernel memory corruption and disclosure
+	vulnerability.  [SA-15:02]
+
+	Fix SCTP stream reset vulnerability.  [SA-15:03]
+
 20150114:	p16	FreeBSD-SA-15:01.openssl
 	Fix multiple vulnerabilities in OpenSSL.  [SA-15:01]
 

Modified: releng/10.0/sys/conf/newvers.sh
==============================================================================
--- releng/10.0/sys/conf/newvers.sh	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.0/sys/conf/newvers.sh	Tue Jan 27 19:37:02 2015	(r277808)
@@ -32,7 +32,7 @@
 
 TYPE="FreeBSD"
 REVISION="10.0"
-BRANCH="RELEASE-p16"
+BRANCH="RELEASE-p17"
 if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
 	BRANCH=${BRANCH_OVERRIDE}
 fi

Modified: releng/10.0/sys/netinet/sctp_input.c
==============================================================================
--- releng/10.0/sys/netinet/sctp_input.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.0/sys/netinet/sctp_input.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -3690,6 +3690,9 @@ sctp_handle_stream_reset_response(struct
 					/* huh ? */
 					return (0);
 				}
+				if (ntohs(respin->ph.param_length) < sizeof(struct sctp_stream_reset_response_tsn)) {
+					return (0);
+				}
 				if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) {
 					resp = (struct sctp_stream_reset_response_tsn *)respin;
 					asoc->stream_reset_outstanding--;
@@ -4078,7 +4081,7 @@ __attribute__((noinline))
 	    sctp_handle_stream_reset(struct sctp_tcb *stcb, struct mbuf *m, int offset,
         struct sctp_chunkhdr *ch_req)
 {
-	int chk_length, param_len, ptype;
+	uint16_t remaining_length, param_len, ptype;
 	struct sctp_paramhdr pstore;
 	uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE];
 	uint32_t seq = 0;
@@ -4091,7 +4094,7 @@ __attribute__((noinline))
 	int num_param = 0;
 
 	/* now it may be a reset or a reset-response */
-	chk_length = ntohs(ch_req->chunk_length);
+	remaining_length = ntohs(ch_req->chunk_length) - sizeof(struct sctp_chunkhdr);
 
 	/* setup for adding the response */
 	sctp_alloc_a_chunk(stcb, chk);
@@ -4127,20 +4130,27 @@ strres_nochunk:
 	ch->chunk_length = htons(chk->send_size);
 	SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
 	offset += sizeof(struct sctp_chunkhdr);
-	while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_tsn_request)) {
+	while (remaining_length >= sizeof(struct sctp_paramhdr)) {
 		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(pstore), (uint8_t *) & pstore);
-		if (ph == NULL)
+		if (ph == NULL) {
+			/* TSNH */
 			break;
+		}
 		param_len = ntohs(ph->param_length);
-		if (param_len < (int)sizeof(struct sctp_stream_reset_tsn_request)) {
-			/* bad param */
+		if ((param_len > remaining_length) ||
+		    (param_len < (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)))) {
+			/* bad parameter length */
 			break;
 		}
-		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, (int)sizeof(cstore)),
+		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, sizeof(cstore)),
 		    (uint8_t *) & cstore);
+		if (ph == NULL) {
+			/* TSNH */
+			break;
+		}
 		ptype = ntohs(ph->param_type);
 		num_param++;
-		if (param_len > (int)sizeof(cstore)) {
+		if (param_len > sizeof(cstore)) {
 			trunc = 1;
 		} else {
 			trunc = 0;
@@ -4152,6 +4162,9 @@ strres_nochunk:
 		if (ptype == SCTP_STR_RESET_OUT_REQUEST) {
 			struct sctp_stream_reset_out_request *req_out;
 
+			if (param_len < sizeof(struct sctp_stream_reset_out_request)) {
+				break;
+			}
 			req_out = (struct sctp_stream_reset_out_request *)ph;
 			num_req++;
 			if (stcb->asoc.stream_reset_outstanding) {
@@ -4165,12 +4178,18 @@ strres_nochunk:
 		} else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_strm(stcb, chk, str_add);
 		} else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_out_strm(stcb, chk, str_add);
@@ -4195,6 +4214,9 @@ strres_nochunk:
 			struct sctp_stream_reset_response *resp;
 			uint32_t result;
 
+			if (param_len < sizeof(struct sctp_stream_reset_response)) {
+				break;
+			}
 			resp = (struct sctp_stream_reset_response *)ph;
 			seq = ntohl(resp->response_seq);
 			result = ntohl(resp->result);
@@ -4206,7 +4228,11 @@ strres_nochunk:
 			break;
 		}
 		offset += SCTP_SIZE32(param_len);
-		chk_length -= SCTP_SIZE32(param_len);
+		if (remaining_length >= SCTP_SIZE32(param_len)) {
+			remaining_length -= SCTP_SIZE32(param_len);
+		} else {
+			remaining_length = 0;
+		}
 	}
 	if (num_req == 0) {
 		/* we have no response free the stuff */

Modified: releng/10.0/sys/netinet/sctp_usrreq.c
==============================================================================
--- releng/10.0/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.0/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -1869,8 +1869,9 @@ flags_out:
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    &av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    &av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				} else {
@@ -3675,8 +3676,9 @@ sctp_setopt(struct socket *so, int optna
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				}
@@ -3686,10 +3688,12 @@ sctp_setopt(struct socket *so, int optna
 					SCTP_INP_RLOCK(inp);
 					LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
 						SCTP_TCB_LOCK(stcb);
-						stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
-						    &stcb->asoc,
-						    &stcb->asoc.strmout[av->stream_id],
-						    av->stream_value);
+						if (av->stream_id < stcb->asoc.streamoutcnt) {
+							stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
+							    &stcb->asoc,
+							    &stcb->asoc.strmout[av->stream_id],
+							    av->stream_value);
+						}
 						SCTP_TCB_UNLOCK(stcb);
 					}
 					SCTP_INP_RUNLOCK(inp);

Modified: releng/10.1/UPDATING
==============================================================================
--- releng/10.1/UPDATING	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.1/UPDATING	Tue Jan 27 19:37:02 2015	(r277808)
@@ -16,6 +16,14 @@ from older versions of FreeBSD, try WITH
 stable/10, and then rebuild without this option. The bootstrap process from
 older version of current is a bit fragile.
 
+20150127:	p5	FreeBSD-SA-15:02.kmem
+			FreeBSD-SA-15:03.sctp
+
+	Fix SCTP SCTP_SS_VALUE kernel memory corruption and disclosure
+	vulnerability.  [SA-15:02]
+
+	Fix SCTP stream reset vulnerability.  [SA-15:03]
+
 20150114:	p4	FreeBSD-SA-15:01.openssl
 	Fix multiple vulnerabilities in OpenSSL.  [SA-15:01]
 

Modified: releng/10.1/sys/conf/newvers.sh
==============================================================================
--- releng/10.1/sys/conf/newvers.sh	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.1/sys/conf/newvers.sh	Tue Jan 27 19:37:02 2015	(r277808)
@@ -32,7 +32,7 @@
 
 TYPE="FreeBSD"
 REVISION="10.1"
-BRANCH="RELEASE-p4"
+BRANCH="RELEASE-p5"
 if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
 	BRANCH=${BRANCH_OVERRIDE}
 fi

Modified: releng/10.1/sys/netinet/sctp_input.c
==============================================================================
--- releng/10.1/sys/netinet/sctp_input.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.1/sys/netinet/sctp_input.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -3664,6 +3664,9 @@ sctp_handle_stream_reset_response(struct
 					/* huh ? */
 					return (0);
 				}
+				if (ntohs(respin->ph.param_length) < sizeof(struct sctp_stream_reset_response_tsn)) {
+					return (0);
+				}
 				if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) {
 					resp = (struct sctp_stream_reset_response_tsn *)respin;
 					asoc->stream_reset_outstanding--;
@@ -4052,7 +4055,7 @@ __attribute__((noinline))
 	    sctp_handle_stream_reset(struct sctp_tcb *stcb, struct mbuf *m, int offset,
         struct sctp_chunkhdr *ch_req)
 {
-	int chk_length, param_len, ptype;
+	uint16_t remaining_length, param_len, ptype;
 	struct sctp_paramhdr pstore;
 	uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE];
 	uint32_t seq = 0;
@@ -4065,7 +4068,7 @@ __attribute__((noinline))
 	int num_param = 0;
 
 	/* now it may be a reset or a reset-response */
-	chk_length = ntohs(ch_req->chunk_length);
+	remaining_length = ntohs(ch_req->chunk_length) - sizeof(struct sctp_chunkhdr);
 
 	/* setup for adding the response */
 	sctp_alloc_a_chunk(stcb, chk);
@@ -4103,20 +4106,27 @@ strres_nochunk:
 	ch->chunk_length = htons(chk->send_size);
 	SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
 	offset += sizeof(struct sctp_chunkhdr);
-	while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_tsn_request)) {
+	while (remaining_length >= sizeof(struct sctp_paramhdr)) {
 		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(pstore), (uint8_t *) & pstore);
-		if (ph == NULL)
+		if (ph == NULL) {
+			/* TSNH */
 			break;
+		}
 		param_len = ntohs(ph->param_length);
-		if (param_len < (int)sizeof(struct sctp_stream_reset_tsn_request)) {
-			/* bad param */
+		if ((param_len > remaining_length) ||
+		    (param_len < (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)))) {
+			/* bad parameter length */
 			break;
 		}
-		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, (int)sizeof(cstore)),
+		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, sizeof(cstore)),
 		    (uint8_t *) & cstore);
+		if (ph == NULL) {
+			/* TSNH */
+			break;
+		}
 		ptype = ntohs(ph->param_type);
 		num_param++;
-		if (param_len > (int)sizeof(cstore)) {
+		if (param_len > sizeof(cstore)) {
 			trunc = 1;
 		} else {
 			trunc = 0;
@@ -4128,6 +4138,9 @@ strres_nochunk:
 		if (ptype == SCTP_STR_RESET_OUT_REQUEST) {
 			struct sctp_stream_reset_out_request *req_out;
 
+			if (param_len < sizeof(struct sctp_stream_reset_out_request)) {
+				break;
+			}
 			req_out = (struct sctp_stream_reset_out_request *)ph;
 			num_req++;
 			if (stcb->asoc.stream_reset_outstanding) {
@@ -4141,12 +4154,18 @@ strres_nochunk:
 		} else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_strm(stcb, chk, str_add);
 		} else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_out_strm(stcb, chk, str_add);
@@ -4171,6 +4190,9 @@ strres_nochunk:
 			struct sctp_stream_reset_response *resp;
 			uint32_t result;
 
+			if (param_len < sizeof(struct sctp_stream_reset_response)) {
+				break;
+			}
 			resp = (struct sctp_stream_reset_response *)ph;
 			seq = ntohl(resp->response_seq);
 			result = ntohl(resp->result);
@@ -4182,7 +4204,11 @@ strres_nochunk:
 			break;
 		}
 		offset += SCTP_SIZE32(param_len);
-		chk_length -= SCTP_SIZE32(param_len);
+		if (remaining_length >= SCTP_SIZE32(param_len)) {
+			remaining_length -= SCTP_SIZE32(param_len);
+		} else {
+			remaining_length = 0;
+		}
 	}
 	if (num_req == 0) {
 		/* we have no response free the stuff */

Modified: releng/10.1/sys/netinet/sctp_usrreq.c
==============================================================================
--- releng/10.1/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/10.1/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -1854,8 +1854,9 @@ flags_out:
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    &av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    &av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				} else {
@@ -3915,8 +3916,9 @@ sctp_setopt(struct socket *so, int optna
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				}
@@ -3926,10 +3928,12 @@ sctp_setopt(struct socket *so, int optna
 					SCTP_INP_RLOCK(inp);
 					LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
 						SCTP_TCB_LOCK(stcb);
-						stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
-						    &stcb->asoc,
-						    &stcb->asoc.strmout[av->stream_id],
-						    av->stream_value);
+						if (av->stream_id < stcb->asoc.streamoutcnt) {
+							stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
+							    &stcb->asoc,
+							    &stcb->asoc.strmout[av->stream_id],
+							    av->stream_value);
+						}
 						SCTP_TCB_UNLOCK(stcb);
 					}
 					SCTP_INP_RUNLOCK(inp);

Modified: releng/8.4/UPDATING
==============================================================================
--- releng/8.4/UPDATING	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/8.4/UPDATING	Tue Jan 27 19:37:02 2015	(r277808)
@@ -15,6 +15,14 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.
 	debugging tools present in HEAD were left in place because
 	sun4v support still needs work to become production ready.
 
+20150127:	p23	FreeBSD-SA-15:02.kmem
+			FreeBSD-SA-15:03.sctp
+
+	Fix SCTP SCTP_SS_VALUE kernel memory corruption and disclosure
+	vulnerability.  [SA-15:02]
+
+	Fix SCTP stream reset vulnerability.  [SA-15:03]
+
 20150114:	p22	FreeBSD-SA-15:01.openssl
 	Fix multiple vulnerabilities in OpenSSL.  [SA-15:01]
 

Modified: releng/8.4/sys/conf/newvers.sh
==============================================================================
--- releng/8.4/sys/conf/newvers.sh	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/8.4/sys/conf/newvers.sh	Tue Jan 27 19:37:02 2015	(r277808)
@@ -32,7 +32,7 @@
 
 TYPE="FreeBSD"
 REVISION="8.4"
-BRANCH="RELEASE-p22"
+BRANCH="RELEASE-p23"
 if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
 	BRANCH=${BRANCH_OVERRIDE}
 fi

Modified: releng/8.4/sys/netinet/sctp_input.c
==============================================================================
--- releng/8.4/sys/netinet/sctp_input.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/8.4/sys/netinet/sctp_input.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -3680,6 +3680,9 @@ sctp_handle_stream_reset_response(struct
 					/* huh ? */
 					return (0);
 				}
+				if (ntohs(respin->ph.param_length) < sizeof(struct sctp_stream_reset_response_tsn)) {
+					return (0);
+				}
 				if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) {
 					resp = (struct sctp_stream_reset_response_tsn *)respin;
 					asoc->stream_reset_outstanding--;
@@ -4068,7 +4071,7 @@ __attribute__((noinline))
 	    sctp_handle_stream_reset(struct sctp_tcb *stcb, struct mbuf *m, int offset,
         struct sctp_chunkhdr *ch_req)
 {
-	int chk_length, param_len, ptype;
+	uint16_t remaining_length, param_len, ptype;
 	struct sctp_paramhdr pstore;
 	uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE];
 	uint32_t seq = 0;
@@ -4081,7 +4084,7 @@ __attribute__((noinline))
 	int num_param = 0;
 
 	/* now it may be a reset or a reset-response */
-	chk_length = ntohs(ch_req->chunk_length);
+	remaining_length = ntohs(ch_req->chunk_length) - sizeof(struct sctp_chunkhdr);
 
 	/* setup for adding the response */
 	sctp_alloc_a_chunk(stcb, chk);
@@ -4117,20 +4120,27 @@ strres_nochunk:
 	ch->chunk_length = htons(chk->send_size);
 	SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
 	offset += sizeof(struct sctp_chunkhdr);
-	while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_tsn_request)) {
+	while (remaining_length >= sizeof(struct sctp_paramhdr)) {
 		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(pstore), (uint8_t *) & pstore);
-		if (ph == NULL)
+		if (ph == NULL) {
+			/* TSNH */
 			break;
+		}
 		param_len = ntohs(ph->param_length);
-		if (param_len < (int)sizeof(struct sctp_stream_reset_tsn_request)) {
-			/* bad param */
+		if ((param_len > remaining_length) ||
+		    (param_len < (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)))) {
+			/* bad parameter length */
 			break;
 		}
-		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, (int)sizeof(cstore)),
+		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, sizeof(cstore)),
 		    (uint8_t *) & cstore);
+		if (ph == NULL) {
+			/* TSNH */
+			break;
+		}
 		ptype = ntohs(ph->param_type);
 		num_param++;
-		if (param_len > (int)sizeof(cstore)) {
+		if (param_len > sizeof(cstore)) {
 			trunc = 1;
 		} else {
 			trunc = 0;
@@ -4142,6 +4152,9 @@ strres_nochunk:
 		if (ptype == SCTP_STR_RESET_OUT_REQUEST) {
 			struct sctp_stream_reset_out_request *req_out;
 
+			if (param_len < sizeof(struct sctp_stream_reset_out_request)) {
+				break;
+			}
 			req_out = (struct sctp_stream_reset_out_request *)ph;
 			num_req++;
 			if (stcb->asoc.stream_reset_outstanding) {
@@ -4155,12 +4168,18 @@ strres_nochunk:
 		} else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_strm(stcb, chk, str_add);
 		} else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_out_strm(stcb, chk, str_add);
@@ -4185,6 +4204,9 @@ strres_nochunk:
 			struct sctp_stream_reset_response *resp;
 			uint32_t result;
 
+			if (param_len < sizeof(struct sctp_stream_reset_response)) {
+				break;
+			}
 			resp = (struct sctp_stream_reset_response *)ph;
 			seq = ntohl(resp->response_seq);
 			result = ntohl(resp->result);
@@ -4196,7 +4218,11 @@ strres_nochunk:
 			break;
 		}
 		offset += SCTP_SIZE32(param_len);
-		chk_length -= SCTP_SIZE32(param_len);
+		if (remaining_length >= SCTP_SIZE32(param_len)) {
+			remaining_length -= SCTP_SIZE32(param_len);
+		} else {
+			remaining_length = 0;
+		}
 	}
 	if (num_req == 0) {
 		/* we have no response free the stuff */

Modified: releng/8.4/sys/netinet/sctp_usrreq.c
==============================================================================
--- releng/8.4/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/8.4/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -1845,8 +1845,9 @@ flags_out:
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    &av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    &av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				} else {
@@ -3640,8 +3641,9 @@ sctp_setopt(struct socket *so, int optna
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				}
@@ -3651,10 +3653,12 @@ sctp_setopt(struct socket *so, int optna
 					SCTP_INP_RLOCK(inp);
 					LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
 						SCTP_TCB_LOCK(stcb);
-						stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
-						    &stcb->asoc,
-						    &stcb->asoc.strmout[av->stream_id],
-						    av->stream_value);
+						if (av->stream_id < stcb->asoc.streamoutcnt) {
+							stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
+							    &stcb->asoc,
+							    &stcb->asoc.strmout[av->stream_id],
+							    av->stream_value);
+						}
 						SCTP_TCB_UNLOCK(stcb);
 					}
 					SCTP_INP_RUNLOCK(inp);

Modified: releng/9.3/UPDATING
==============================================================================
--- releng/9.3/UPDATING	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/9.3/UPDATING	Tue Jan 27 19:37:02 2015	(r277808)
@@ -11,6 +11,14 @@ handbook:
 Items affecting the ports and packages system can be found in
 /usr/ports/UPDATING.  Please read that file before running portupgrade.
 
+20150127:	p9	FreeBSD-SA-15:02.kmem
+			FreeBSD-SA-15:03.sctp
+
+	Fix SCTP SCTP_SS_VALUE kernel memory corruption and disclosure
+	vulnerability.  [SA-15:02]
+
+	Fix SCTP stream reset vulnerability.  [SA-15:03]
+
 20150114:	p8	FreeBSD-SA-15:01.openssl
 	Fix multiple vulnerabilities in OpenSSL.  [SA-15:01]
 

Modified: releng/9.3/sys/conf/newvers.sh
==============================================================================
--- releng/9.3/sys/conf/newvers.sh	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/9.3/sys/conf/newvers.sh	Tue Jan 27 19:37:02 2015	(r277808)
@@ -32,7 +32,7 @@
 
 TYPE="FreeBSD"
 REVISION="9.3"
-BRANCH="RELEASE-p8"
+BRANCH="RELEASE-p9"
 if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
 	BRANCH=${BRANCH_OVERRIDE}
 fi

Modified: releng/9.3/sys/netinet/sctp_input.c
==============================================================================
--- releng/9.3/sys/netinet/sctp_input.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/9.3/sys/netinet/sctp_input.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -3662,6 +3662,9 @@ sctp_handle_stream_reset_response(struct
 					/* huh ? */
 					return (0);
 				}
+				if (ntohs(respin->ph.param_length) < sizeof(struct sctp_stream_reset_response_tsn)) {
+					return (0);
+				}
 				if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) {
 					resp = (struct sctp_stream_reset_response_tsn *)respin;
 					asoc->stream_reset_outstanding--;
@@ -4050,7 +4053,7 @@ __attribute__((noinline))
 	    sctp_handle_stream_reset(struct sctp_tcb *stcb, struct mbuf *m, int offset,
         struct sctp_chunkhdr *ch_req)
 {
-	int chk_length, param_len, ptype;
+	uint16_t remaining_length, param_len, ptype;
 	struct sctp_paramhdr pstore;
 	uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE];
 	uint32_t seq = 0;
@@ -4063,7 +4066,7 @@ __attribute__((noinline))
 	int num_param = 0;
 
 	/* now it may be a reset or a reset-response */
-	chk_length = ntohs(ch_req->chunk_length);
+	remaining_length = ntohs(ch_req->chunk_length) - sizeof(struct sctp_chunkhdr);
 
 	/* setup for adding the response */
 	sctp_alloc_a_chunk(stcb, chk);
@@ -4099,20 +4102,27 @@ strres_nochunk:
 	ch->chunk_length = htons(chk->send_size);
 	SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
 	offset += sizeof(struct sctp_chunkhdr);
-	while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_tsn_request)) {
+	while (remaining_length >= sizeof(struct sctp_paramhdr)) {
 		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(pstore), (uint8_t *) & pstore);
-		if (ph == NULL)
+		if (ph == NULL) {
+			/* TSNH */
 			break;
+		}
 		param_len = ntohs(ph->param_length);
-		if (param_len < (int)sizeof(struct sctp_stream_reset_tsn_request)) {
-			/* bad param */
+		if ((param_len > remaining_length) ||
+		    (param_len < (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)))) {
+			/* bad parameter length */
 			break;
 		}
-		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, (int)sizeof(cstore)),
+		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, sizeof(cstore)),
 		    (uint8_t *) & cstore);
+		if (ph == NULL) {
+			/* TSNH */
+			break;
+		}
 		ptype = ntohs(ph->param_type);
 		num_param++;
-		if (param_len > (int)sizeof(cstore)) {
+		if (param_len > sizeof(cstore)) {
 			trunc = 1;
 		} else {
 			trunc = 0;
@@ -4124,6 +4134,9 @@ strres_nochunk:
 		if (ptype == SCTP_STR_RESET_OUT_REQUEST) {
 			struct sctp_stream_reset_out_request *req_out;
 
+			if (param_len < sizeof(struct sctp_stream_reset_out_request)) {
+				break;
+			}
 			req_out = (struct sctp_stream_reset_out_request *)ph;
 			num_req++;
 			if (stcb->asoc.stream_reset_outstanding) {
@@ -4137,12 +4150,18 @@ strres_nochunk:
 		} else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_strm(stcb, chk, str_add);
 		} else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) {
 			struct sctp_stream_reset_add_strm *str_add;
 
+			if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+				break;
+			}
 			str_add = (struct sctp_stream_reset_add_strm *)ph;
 			num_req++;
 			sctp_handle_str_reset_add_out_strm(stcb, chk, str_add);
@@ -4167,6 +4186,9 @@ strres_nochunk:
 			struct sctp_stream_reset_response *resp;
 			uint32_t result;
 
+			if (param_len < sizeof(struct sctp_stream_reset_response)) {
+				break;
+			}
 			resp = (struct sctp_stream_reset_response *)ph;
 			seq = ntohl(resp->response_seq);
 			result = ntohl(resp->result);
@@ -4178,7 +4200,11 @@ strres_nochunk:
 			break;
 		}
 		offset += SCTP_SIZE32(param_len);
-		chk_length -= SCTP_SIZE32(param_len);
+		if (remaining_length >= SCTP_SIZE32(param_len)) {
+			remaining_length -= SCTP_SIZE32(param_len);
+		} else {
+			remaining_length = 0;
+		}
 	}
 	if (num_req == 0) {
 		/* we have no response free the stuff */

Modified: releng/9.3/sys/netinet/sctp_usrreq.c
==============================================================================
--- releng/9.3/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:36:08 2015	(r277807)
+++ releng/9.3/sys/netinet/sctp_usrreq.c	Tue Jan 27 19:37:02 2015	(r277808)
@@ -1854,8 +1854,9 @@ flags_out:
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    &av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    &av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				} else {
@@ -3660,8 +3661,9 @@ sctp_setopt(struct socket *so, int optna
 			SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize);
 			SCTP_FIND_STCB(inp, stcb, av->assoc_id);
 			if (stcb) {
-				if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
-				    av->stream_value) < 0) {
+				if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+				    (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+				    av->stream_value) < 0)) {
 					SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
 					error = EINVAL;
 				}
@@ -3671,10 +3673,12 @@ sctp_setopt(struct socket *so, int optna
 					SCTP_INP_RLOCK(inp);
 					LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
 						SCTP_TCB_LOCK(stcb);
-						stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
-						    &stcb->asoc,
-						    &stcb->asoc.strmout[av->stream_id],
-						    av->stream_value);
+						if (av->stream_id < stcb->asoc.streamoutcnt) {
+							stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
+							    &stcb->asoc,
+							    &stcb->asoc.strmout[av->stream_id],
+							    av->stream_value);
+						}
 						SCTP_TCB_UNLOCK(stcb);
 					}
 					SCTP_INP_RUNLOCK(inp);



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