svn commit: r366674 - head/sys/kern

John Baldwin jhb at FreeBSD.org
Tue Oct 13 17:30:35 UTC 2020


Author: jhb
Date: Tue Oct 13 17:30:34 2020
New Revision: 366674
URL: https://svnweb.freebsd.org/changeset/base/366674

Log:
  Permit sending empty fragments for TLS 1.0.
  
  Due to a weakness in the TLS 1.0 protocol, OpenSSL will periodically
  send empty TLS records ("empty fragments").  These TLS records have no
  payload (and thus a page count of zero).  m_uiotombuf_nomap() was
  returning NULL instead of an empty mbuf, and a few places needed to be
  updated to treat an empty TLS record as having a page count of "1" as
  0 means "no work to do" (e.g. nothing to encrypt, or nothing to mark
  ready via sbready()).
  
  Reviewed by:	gallatin
  Sponsored by:	Netflix
  Differential Revision:	https://reviews.freebsd.org/D26729

Modified:
  head/sys/kern/uipc_ktls.c
  head/sys/kern/uipc_mbuf.c
  head/sys/kern/uipc_sockbuf.c

Modified: head/sys/kern/uipc_ktls.c
==============================================================================
--- head/sys/kern/uipc_ktls.c	Tue Oct 13 17:27:37 2020	(r366673)
+++ head/sys/kern/uipc_ktls.c	Tue Oct 13 17:30:34 2020	(r366674)
@@ -1384,7 +1384,9 @@ ktls_seq(struct sockbuf *sb, struct mbuf *m)
  * The enq_count argument on return is set to the number of pages of
  * payload data for this entire chain that need to be encrypted via SW
  * encryption.  The returned value should be passed to ktls_enqueue
- * when scheduling encryption of this chain of mbufs.
+ * when scheduling encryption of this chain of mbufs.  To handle the
+ * special case of empty fragments for TLS 1.0 sessions, an empty
+ * fragment counts as one page.
  */
 void
 ktls_frame(struct mbuf *top, struct ktls_session *tls, int *enq_cnt,
@@ -1400,12 +1402,16 @@ ktls_frame(struct mbuf *top, struct ktls_session *tls,
 	*enq_cnt = 0;
 	for (m = top; m != NULL; m = m->m_next) {
 		/*
-		 * All mbufs in the chain should be non-empty TLS
-		 * records whose payload does not exceed the maximum
-		 * frame length.
+		 * All mbufs in the chain should be TLS records whose
+		 * payload does not exceed the maximum frame length.
+		 *
+		 * Empty TLS records are permitted when using CBC.
 		 */
-		KASSERT(m->m_len <= maxlen && m->m_len > 0,
+		KASSERT(m->m_len <= maxlen &&
+		    (tls->params.cipher_algorithm == CRYPTO_AES_CBC ?
+		    m->m_len >= 0 : m->m_len > 0),
 		    ("ktls_frame: m %p len %d\n", m, m->m_len));
+
 		/*
 		 * TLS frames require unmapped mbufs to store session
 		 * info.
@@ -1496,7 +1502,11 @@ ktls_frame(struct mbuf *top, struct ktls_session *tls,
 		if (tls->mode == TCP_TLS_MODE_SW) {
 			m->m_flags |= M_NOTREADY;
 			m->m_epg_nrdy = m->m_epg_npgs;
-			*enq_cnt += m->m_epg_npgs;
+			if (__predict_false(tls_len == 0)) {
+				/* TLS 1.0 empty fragment. */
+				*enq_cnt += 1;
+			} else
+				*enq_cnt += m->m_epg_npgs;
 		}
 	}
 }
@@ -1961,7 +1971,11 @@ retry_page:
 			dst_iov[i].iov_len = len;
 		}
 
-		npages += i;
+		if (__predict_false(m->m_epg_npgs == 0)) {
+			/* TLS 1.0 empty fragment. */
+			npages++;
+		} else
+			npages += i;
 
 		error = (*tls->sw_encrypt)(tls,
 		    (const struct tls_record_layer *)m->m_epg_hdr,

Modified: head/sys/kern/uipc_mbuf.c
==============================================================================
--- head/sys/kern/uipc_mbuf.c	Tue Oct 13 17:27:37 2020	(r366673)
+++ head/sys/kern/uipc_mbuf.c	Tue Oct 13 17:30:34 2020	(r366674)
@@ -1655,6 +1655,8 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, i
 	int pflags = malloc2vm_flags(how) | VM_ALLOC_NOOBJ | VM_ALLOC_NODUMP |
 	    VM_ALLOC_WIRED;
 
+	MPASS((flags & M_PKTHDR) == 0);
+
 	/*
 	 * len can be zero or an arbitrary large value bound by
 	 * the total data supplied by the uio.
@@ -1668,10 +1670,23 @@ m_uiotombuf_nomap(struct uio *uio, int how, int len, i
 		maxseg = MBUF_PEXT_MAX_PGS * PAGE_SIZE;
 
 	/*
+	 * If total is zero, return an empty mbuf.  This can occur
+	 * for TLS 1.0 connections which send empty fragments as
+	 * a countermeasure against the known-IV weakness in CBC
+	 * ciphersuites.
+	 */
+	if (__predict_false(total == 0)) {
+		mb = mb_alloc_ext_pgs(how, mb_free_mext_pgs);
+		if (mb == NULL)
+			return (NULL);
+		mb->m_epg_flags = EPG_FLAG_ANON;
+		return (mb);
+	}
+
+	/*
 	 * Allocate the pages
 	 */
 	m = NULL;
-	MPASS((flags & M_PKTHDR) == 0);
 	while (total > 0) {
 		mb = mb_alloc_ext_pgs(how, mb_free_mext_pgs);
 		if (mb == NULL)

Modified: head/sys/kern/uipc_sockbuf.c
==============================================================================
--- head/sys/kern/uipc_sockbuf.c	Tue Oct 13 17:27:37 2020	(r366673)
+++ head/sys/kern/uipc_sockbuf.c	Tue Oct 13 17:30:34 2020	(r366674)
@@ -212,7 +212,7 @@ sbready(struct sockbuf *sb, struct mbuf *m0, int count
 	while (count > 0) {
 		KASSERT(m->m_flags & M_NOTREADY,
 		    ("%s: m %p !M_NOTREADY", __func__, m));
-		if ((m->m_flags & M_EXTPG) != 0) {
+		if ((m->m_flags & M_EXTPG) != 0 && m->m_epg_npgs != 0) {
 			if (count < m->m_epg_nrdy) {
 				m->m_epg_nrdy -= count;
 				count = 0;


More information about the svn-src-all mailing list