Browse Source

chore: cherry-pick 2c26785, b03de8b and a3c3ef6 from usrsctp. (#27493)

* chore: cherry-pick 2c26785, b03de8b and a3c3ef6 from usrsctp.

* update patches

Co-authored-by: Electron Bot <[email protected]>
Pedro Pontes 4 years ago
parent
commit
35dfdfc901

+ 3 - 0
patches/usrsctp/.patches

@@ -1 +1,4 @@
 fix_a_use-after-free_bug_for_the_userland_stack.patch
+cherry_picking_improve_the_input_validation_and_processing_of.patch
+cherry_picking_clean_up_more_resources_of_an_existing_sctp.patch
+cherry_picking_harden_the_handling_of_outgoing_streams.patch

+ 104 - 0
patches/usrsctp/cherry_picking_clean_up_more_resources_of_an_existing_sctp.patch

@@ -0,0 +1,104 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Tuexen <[email protected]>
+Date: Sat, 12 Dec 2020 23:30:59 +0100
+Subject: Cherry picking: 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.
+
+diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c
+index b8e1259d8cf0c90cf2dcb03a1fd853c11a6abbd3..5f16f85add50796a2f28227d89cc397d01ed10d4 100755
+--- a/usrsctplib/netinet/sctp_input.c
++++ b/usrsctplib/netinet/sctp_input.c
+@@ -34,7 +34,7 @@
+ 
+ #ifdef __FreeBSD__
+ #include <sys/cdefs.h>
+-__FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 366248 2020-09-29 09:36:06Z tuexen $");
++__FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 368593 2020-12-12 22:23:45Z tuexen $");
+ #endif
+ 
+ #include <netinet/sctp_os.h>
+@@ -1607,6 +1607,11 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
+ 	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;
+@@ -1903,8 +1908,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
+ 			 * kick us so it COULD 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) {
++			TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+ 				if (chk->sent < SCTP_DATAGRAM_RESEND) {
+ 					chk->sent = SCTP_DATAGRAM_RESEND;
+ 					sctp_flight_size_decrease(chk);
+@@ -2096,6 +2100,57 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
+ 			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);

+ 54 - 0
patches/usrsctp/cherry_picking_harden_the_handling_of_outgoing_streams.patch

@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Tuexen <[email protected]>
+Date: Mon, 14 Dec 2020 00:57:51 +0100
+Subject: Cherry picking: 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.
+
+diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c
+index 5f16f85add50796a2f28227d89cc397d01ed10d4..d538c347b967b89d99c7cdaf1fef173107319783 100755
+--- a/usrsctplib/netinet/sctp_input.c
++++ b/usrsctplib/netinet/sctp_input.c
+@@ -34,7 +34,7 @@
+ 
+ #ifdef __FreeBSD__
+ #include <sys/cdefs.h>
+-__FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 368593 2020-12-12 22:23:45Z tuexen $");
++__FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 368622 2020-12-13 23:51:51Z tuexen $");
+ #endif
+ 
+ #include <netinet/sctp_os.h>
+@@ -1898,7 +1898,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
+ 					 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) {
+ 			/* Ok the peer probably discarded our
+@@ -2052,8 +2054,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
+ 			/* 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;
+@@ -2373,7 +2376,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
+ 	/* 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;

+ 127 - 0
patches/usrsctp/cherry_picking_improve_the_input_validation_and_processing_of.patch

@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Tuexen <[email protected]>
+Date: Tue, 29 Sep 2020 11:38:11 +0200
+Subject: Cherry picking: Improve the input validation and processing of
+ cookies.
+
+This avoids setting the association in an inconsistent
+state, which could result in a use-after-free situation.
+The issue can be triggered by a malicious peer, if the peer
+can modify the cookie without the local endpoint recognizing it.
+
+Thanks to Ned Williamson for reporting the issue.
+
+diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c
+index b7e4aea13613bef798be7cddc1e72d4c42afac8e..b8e1259d8cf0c90cf2dcb03a1fd853c11a6abbd3 100755
+--- a/usrsctplib/netinet/sctp_input.c
++++ b/usrsctplib/netinet/sctp_input.c
+@@ -34,7 +34,7 @@
+ 
+ #ifdef __FreeBSD__
+ #include <sys/cdefs.h>
+-__FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 355135 2019-11-27 19:32:29Z tuexen $");
++__FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 366248 2020-09-29 09:36:06Z tuexen $");
+ #endif
+ 
+ #include <netinet/sctp_os.h>
+@@ -2272,10 +2272,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
+ 		                       vrf_id, port);
+ 		return (NULL);
+ 	}
+-	/* get the correct sctp_nets */
+-	if (netp)
+-		*netp = sctp_findnet(stcb, init_src);
+-
+ 	asoc = &stcb->asoc;
+ 	/* get scope variables out of cookie */
+ 	asoc->scope.ipv4_local_scope = cookie->ipv4_scope;
+@@ -2332,10 +2328,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
+ 	asoc->advanced_peer_ack_point = asoc->last_acked_seq;
+ 
+ 	/* process the INIT info (peer's info) */
+-	if (netp)
+-		retval = sctp_process_init(init_cp, stcb);
+-	else
+-		retval = 0;
++	retval = sctp_process_init(init_cp, stcb);
+ 	if (retval < 0) {
+ #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+ 		atomic_add_int(&stcb->asoc.refcnt, 1);
+@@ -2518,19 +2511,21 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
+ 		 */
+ 		;
+ 	}
+-	/* since we did not send a HB make sure we don't double things */
+-	if ((netp) && (*netp))
+-		(*netp)->hb_responded = 1;
+-
+ 	if (stcb->asoc.sctp_autoclose_ticks &&
+ 	    sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) {
+ 		sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL);
+ 	}
+ 	(void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
+-	if ((netp != NULL) && (*netp != NULL)) {
++	*netp = sctp_findnet(stcb, init_src);
++	if (*netp != NULL) {
+ 		struct timeval old;
+ 
+-		/* calculate the RTT and set the encaps port */
++		/*
++		 * Since we did not send a HB, make sure we don't double
++		 * things.
++		 */
++		(*netp)->hb_responded = 1;
++		/* Calculate the RTT. */
+ 		old.tv_sec = cookie->time_entered.tv_sec;
+ 		old.tv_usec = cookie->time_entered.tv_usec;
+ 		sctp_calculate_rto(stcb, asoc, *netp, &old, SCTP_RTT_FROM_NON_DATA);
+diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c
+index 02779e536c14ac7fb707e1b30cac1ab72ee3ec66..cae35aedfd18df2cdfff82006b3e09250ed33b41 100755
+--- a/usrsctplib/netinet/sctp_pcb.c
++++ b/usrsctplib/netinet/sctp_pcb.c
+@@ -34,7 +34,7 @@
+ 
+ #ifdef __FreeBSD__
+ #include <sys/cdefs.h>
+-__FBSDID("$FreeBSD: head/sys/netinet/sctp_pcb.c 356377 2020-01-05 14:06:40Z tuexen $");
++__FBSDID("$FreeBSD: head/sys/netinet/sctp_pcb.c 366248 2020-09-29 09:36:06Z tuexen $");
+ #endif
+ 
+ #include <netinet/sctp_os.h>
+@@ -5011,7 +5011,15 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
+ 		if ((ntohs(sin->sin_port) == 0) ||
+ 		    (sin->sin_addr.s_addr == INADDR_ANY) ||
+ 		    (sin->sin_addr.s_addr == INADDR_BROADCAST) ||
+-		    IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
++		    IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) ||
++#if defined(__Userspace__)
++		    (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_CONN) != 0) ||
++		     (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) != 0) &&
++		      (SCTP_IPV6_V6ONLY(inp) != 0)))) {
++#else
++		    (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) != 0) &&
++		     (SCTP_IPV6_V6ONLY(inp) != 0))) {
++#endif
+ 			/* Invalid address */
+ 			SCTP_INP_RUNLOCK(inp);
+ 			SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL);
+@@ -5030,7 +5038,8 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
+ 		sin6 = (struct sockaddr_in6 *)firstaddr;
+ 		if ((ntohs(sin6->sin6_port) == 0) ||
+ 		    IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
+-		    IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
++		    IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) ||
++		    ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0)) {
+ 			/* Invalid address */
+ 			SCTP_INP_RUNLOCK(inp);
+ 			SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL);
+@@ -5048,7 +5057,8 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
+ 
+ 		sconn = (struct sockaddr_conn *)firstaddr;
+ 		if ((ntohs(sconn->sconn_port) == 0) ||
+-		    (sconn->sconn_addr == NULL)) {
++		    (sconn->sconn_addr == NULL) ||
++		    ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_CONN) == 0)) {
+ 			/* Invalid address */
+ 			SCTP_INP_RUNLOCK(inp);
+ 			SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL);