git: fc1a02c93ba4 - stable/14 - ktls: Fix an off-by-one bug in tls13_find_record_type()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 28 Sep 2026 16:20:36 UTC
The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=fc1a02c93ba4c9a98e329a4166618bbaf17d584b

commit fc1a02c93ba4c9a98e329a4166618bbaf17d584b
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-09-23 16:53:17 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-28 13:57:00 +0000

    ktls: Fix an off-by-one bug in tls13_find_record_type()
    
    If the entire plaintext is zero-filled, the backwards walk in
    tls13_find_record_type() would return the offset of the last byte of the
    TLS header.  This causes an underflow when decrypting, resulting in a
    null pointer dereference.
    
    Fix the bug and add a regression test.
    
    Reviewed by:    gallatin, jhb
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D59767
    
    (cherry picked from commit 7c5e457d3afdc7742ee24f0b5ee6e5e7aa00a6bd)
---
 sys/kern/uipc_ktls.c       |  4 +--
 tests/sys/kern/ktls_test.c | 71 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c
index 0273a82dd9fc..2efb1c4cfe0d 100644
--- a/sys/kern/uipc_ktls.c
+++ b/sys/kern/uipc_ktls.c
@@ -2327,14 +2327,14 @@ tls13_find_record_type(struct ktls_session *tls, struct mbuf *m, int tls_len,
 			m_len--;
 		if (m_len > 0) {
 			record_type = cp[m_len - 1];
-			last_offset = offset + m_len;
+			last_offset = offset + m_len - 1;
 		}
 	}
 	if (last_offset < tls->params.tls_hlen)
 		return (EBADMSG);
 
 	*record_typep = record_type;
-	*trailer_len = tls_len - last_offset + 1;
+	*trailer_len = tls_len - last_offset;
 	return (0);
 }
 
diff --git a/tests/sys/kern/ktls_test.c b/tests/sys/kern/ktls_test.c
index d3a79aeb1470..7e9334c10144 100644
--- a/tests/sys/kern/ktls_test.c
+++ b/tests/sys/kern/ktls_test.c
@@ -2756,6 +2756,62 @@ ATF_TC_BODY(ktls_receive_##cipher_name##_bad_type, tc)			\
 	    auth_alg, minor)						\
 	ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_type);
 
+static void
+test_ktls13_receive_no_content_type(const atf_tc_t *tc, struct tls_enable *en,
+    uint64_t seqno)
+{
+	char *outbuf;
+	size_t outbuf_cap, outbuf_len;
+	ssize_t rv;
+	int sockets[2];
+
+	ATF_REQUIRE_INTEQ(TLS_MINOR_VER_THREE, en->tls_vminor);
+
+	outbuf_cap = tls_header_len(en) + tls_trailer_len(en);
+	outbuf = malloc(outbuf_cap);
+
+	ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+	ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+	    sizeof(*en)) == 0);
+	check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+	fd_set_blocking(sockets[0]);
+	fd_set_blocking(sockets[1]);
+
+	outbuf_len = encrypt_tls_record(tc, en, 0 /* invalid content type */,
+	    seqno, NULL, 0, outbuf, outbuf_cap, 0);
+
+	rv = write(sockets[1], outbuf, outbuf_len);
+	ATF_REQUIRE_INTEQ((ssize_t)outbuf_len, rv);
+
+	ktls_receive_tls_error(sockets[0], EBADMSG);
+
+	free(outbuf);
+
+	close_sockets_ignore_errors(sockets);
+}
+
+#define GEN_RECEIVE_NO_CONTENT_TYPE_TEST(cipher_name, cipher_alg,	\
+	    key_size, auth_alg, minor)					\
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_no_content_type);	\
+ATF_TC_BODY(ktls_receive_##cipher_name##_no_content_type, tc)		\
+{									\
+	struct tls_enable en;						\
+	uint64_t seqno;							\
+									\
+	ATF_REQUIRE_KTLS_RX();						\
+	seqno = random();						\
+	build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor,	\
+	    seqno, &en);						\
+	test_ktls13_receive_no_content_type(tc, &en, seqno);		\
+	free_tls_enable(&en);						\
+}
+
+#define ADD_RECEIVE_NO_CONTENT_TYPE_TEST(cipher_name, cipher_alg,	\
+	    key_size, auth_alg, minor)					\
+	ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_no_content_type);
+
 #define GEN_RECEIVE_TLS13_TESTS(cipher_name, cipher_alg, key_size,	\
 	    auth_alg, minor)						\
 	GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size,	\
@@ -2763,7 +2819,9 @@ ATF_TC_BODY(ktls_receive_##cipher_name##_bad_type, tc)			\
 	GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size,	\
 	    auth_alg, minor, long_padded, 64 * 1024, 15)		\
 	GEN_RECEIVE_BAD_TYPE_TEST(cipher_name, cipher_alg, key_size,	\
-	    auth_alg, minor, 64)
+	    auth_alg, minor, 64)					\
+	GEN_RECEIVE_NO_CONTENT_TYPE_TEST(cipher_name, cipher_alg,	\
+	    key_size, auth_alg, minor)
 
 #define ADD_RECEIVE_TLS13_TESTS(cipher_name, cipher_alg, key_size,	\
 	    auth_alg, minor)						\
@@ -2772,12 +2830,15 @@ ATF_TC_BODY(ktls_receive_##cipher_name##_bad_type, tc)			\
 	ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size,	\
 	    auth_alg, minor, long_padded)				\
 	ADD_RECEIVE_BAD_TYPE_TEST(cipher_name, cipher_alg, key_size,	\
-	    auth_alg, minor)
+	    auth_alg, minor)						\
+	ADD_RECEIVE_NO_CONTENT_TYPE_TEST(cipher_name, cipher_alg,	\
+	    key_size, auth_alg, minor)
 
 /*
- * For TLS 1.3 cipher suites, run two additional receive tests which
- * use add padding to each record.  Also run a test that uses an
- * invalid "outer" record type.
+ * For TLS 1.3 cipher suites, run two additional receive tests: two which add
+ * padding to each record, a test that uses an invalid "outer" record type, and
+ * a test that exercises handling of a payload with an invalid inner content
+ * type.
  */
 TLS_13_TESTS(GEN_RECEIVE_TLS13_TESTS);