CVE-2019-8460: Other (NVD-CWE-Other)
value: OpenBSD kernel version <= 6.5 can be forced to create long chains of TCP SACK holes that causes very expensive calls to tcp_sack_option() for every incoming SACK packet which can lead to a denial of service.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 7.5
Published: 2019-08-26T20:15Z
Repository: https://github.com/openbsd/src
Language: C
Files changed: sys/netinet/tcp.h, sys/netinet/tcp_input.c
Commit date: 2019-07-10 18:45:31 +0000
Commit message: Received SACK options are managed by a linked list at the TCP socket.
There is a global tunable limit net.inet.tcp.sackholelimit, default
is 32768.  If an attacker manages to attach all these sack holes
to a few TCP connections, the lists may grow long.  Traversing them
might cause higher CPU consumption on the victim machine.  In
practice such a situation is hard to create as the TCP retransmit
and 2*msl timer flush the list periodically.  For additional
protection, enforce a per connection limit of 128 SACK holes in the
list.
reported by Reuven Plevinsky and Tal Vainshtein
discussed with claudio@ and procter@; OK deraadt@

Diff: diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h
index ab62ec0bc343..3fb7f47a0110 100644
--- a/sys/netinet/tcp.h
+++ b/sys/netinet/tcp.h
@@ -1,6 +1,6 @@
-/*	$OpenBSD: tcp.h,v 1.20 2013/08/12 21:57:16 bluhm Exp $	*/
+/*	$OpenBSD: tcp.h,v 1.21 2019/07/10 18:45:31 bluhm Exp $	*/
 /*	$NetBSD: tcp.h,v 1.8 1995/04/17 05:32:58 cgd Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -100,11 +100,12 @@ struct tcphdr {
 #define	TCPOPT_SACK_PERMIT_HDR \
 (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_SACK_PERMITTED<<8|TCPOLEN_SACK_PERMITTED)
 #define	TCPOPT_SACK_HDR   (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_SACK<<8)
 /* Miscellaneous constants */
 #define	MAX_SACK_BLKS	6	/* Max # SACK blocks stored at sender side */
-#define	TCP_MAX_SACK	3	/* MAX # SACKs sent in any segment */
+#define	TCP_MAX_SACK	3	/* Max # SACKs sent in any segment */
+#define	TCP_SACKHOLE_LIMIT 128	/* Max # SACK holes per connection */
 
 #define	TCP_MAXBURST	4	/* Max # packets after leaving Fast Rxmit */
 
 /*
  * Default maximum segment size for TCP.
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 0244e2907aba..1726d653da01 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1,6 +1,6 @@
-/*	$OpenBSD: tcp_input.c,v 1.359 2018/09/17 14:07:48 friehm Exp $	*/
+/*	$OpenBSD: tcp_input.c,v 1.360 2019/07/10 18:45:31 bluhm Exp $	*/
 /*	$NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -2490,10 +2490,12 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
 			    SEQ_GT(cur->end, sack.end)) {
 				/*
 				 * ACKs some data in middle of a hole; need to
 				 * split current hole
 				 */
+				if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
+					goto done;
 				temp = (struct sackhole *)
 				    pool_get(&sackhl_pool, PR_NOWAIT);
 				if (temp == NULL)
 					goto done; /* ENOBUFS */
 				temp->next = cur->next;
@@ -2517,10 +2519,12 @@ tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
 		if (SEQ_LT(tp->rcv_lastsack, sack.start)) {
 			/*
 			 * Need to append new hole at end.
 			 * Last hole is p (and it's not NULL).
 			 */
+			if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
+				goto done;
 			temp = (struct sackhole *)
 			    pool_get(&sackhl_pool, PR_NOWAIT);
 			if (temp == NULL)
 				goto done; /* ENOBUFS */
 			temp->start = tp->rcv_lastsack;

Vulnerable code: /*	$OpenBSD: tcp.h,v 1.20 2013/08/12 21:57:16 bluhm Exp $	*/
#define	TCP_MAX_SACK	3	/* MAX # SACKs sent in any segment */
/*	$OpenBSD: tcp_input.c,v 1.359 2018/09/17 14:07:48 friehm Exp $	*/
Fixed code: /*	$OpenBSD: tcp.h,v 1.21 2019/07/10 18:45:31 bluhm Exp $	*/
#define	TCP_MAX_SACK	3	/* Max # SACKs sent in any segment */
#define	TCP_SACKHOLE_LIMIT 128	/* Max # SACK holes per connection */
/*	$OpenBSD: tcp_input.c,v 1.360 2019/07/10 18:45:31 bluhm Exp $	*/
				if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
					goto done;
			if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
				goto done;