From nobody Tue Jun 9 19:16:47 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gZdv40tM7z6gTR8 for ; Tue, 09 Jun 2026 19:16:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gZdv356Bjz3JlT for ; Tue, 09 Jun 2026 19:16:47 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032607; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=4cDynXAn8CHJgtZLhsn/WLG2LLOw+iywLz8YuGelp3w=; b=mh+lzrdut8oAptoKF4kPbFJhl3wBhUCA0gEG+WQrWO2Px1kdkn6J/v8sx6fX5VbrsySugN q4/2N82cos6Zj9gc2GAv2IOxHh4+mqwP6XEOu6KEMRBusIADM1OmQZDO0RDXEs7TkNEkko daP1s6sV8N6eamFXFQJa/H8w9Uhi9XtTy6N1d2xTwdrk8lIcvmhM69BATpG9vdvqNpHKfY +/OKM7KPQ+/FgH8wm8Y30Gw+YRROOOR4v+UQSPhW1/5+2frLh5frxk7+os/ZZ4fRln3zMT oYnGuO48RvFs6boY6nHTE0CztcL1+Iurbg1rV4Cp8aK9LPpz78RjzxQMOmzw9A== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781032607; a=rsa-sha256; cv=none; b=pBI7xK5ZapNUEFZXROxoPmSjFytkuenAHAmh20Qo7Khg3G2hWVk60oEAD4MCxaQAkULM4w jZ3tVQePPXj+F0aA/xS4dNgwLoKNDcSAX5Fr3F37qv4dZkhDOoo8aYjS6hBSgZ+5mGDsRU 5TIjIYgcCU3Ww68NcXEQfkEwRU15PHu971UOgd+3K47yykeCB6AwvNuwb9zQ0BjNLhnvGb 4RgMIYPS51W9IehU3D2TvBQm0J5POJ+O3H9g2eRtnLcuIUjZbxG6ybYuZjJ5o2BobyTwoX kozcw/4nwIJDWw8lsdQYHaQ1eIXaVGH1+vHknfBMv+J7UvO6p96kFAN479D4oQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032607; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=4cDynXAn8CHJgtZLhsn/WLG2LLOw+iywLz8YuGelp3w=; b=dO/2jss03tBDH3j+SKLIbbM3+yMEpgAVYQZodKXB33Xs9J7S7jgSa5j2Fx3bav7h6qIgte zkKak60gF4A4zDax+UaWRv/zDQTFvdCVlt+G1lIe5eh2j/v06N6vPS+b47WL3GE5tn1e0n IjocAO5/SxFModeFtQYEoNWtJ4BuZ8v3Pcs5aF/xxwddBC8jJnPEGU3PZ5BsaXY2+TtFBZ lQRwvBnFRyZuA7CiLjEou6zhOB+L8VjBgjm03yl5eAIFVod2pt6/Wq3jqW/zmIss511MeD QQOnwQxwQjoXr8t7nAsbbpS4lFXewijBSkdm+lbid69Df8q6kiUYDXdzdToSlA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gZdv34QQJznvf for ; Tue, 09 Jun 2026 19:16:47 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3e490 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 09 Jun 2026 19:16:47 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: John Baldwin From: Mark Johnston Subject: git: 3444414cb463 - main - ktls: Don't attempt to modify non-anonymous mbufs on the receive path List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 3444414cb4639ef2028abd9b46641e76eadf363d Auto-Submitted: auto-generated Date: Tue, 09 Jun 2026 19:16:47 +0000 Message-Id: <6a28669f.3e490.26fe08e8@gitrepo.freebsd.org> The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=3444414cb4639ef2028abd9b46641e76eadf363d commit 3444414cb4639ef2028abd9b46641e76eadf363d Author: John Baldwin AuthorDate: 2026-06-03 23:22:00 +0000 Commit: Mark Johnston CommitDate: 2026-06-09 19:13:21 +0000 ktls: Don't attempt to modify non-anonymous mbufs on the receive path Normally, data processed on the KTLS receive path is contained in anonymous mbufs that can be modified in place. Either the data originates in receive buffers from a NIC driver, or for loopback connections the data is anonymous-backed mbufs created when writing to a socket. One potential source of non-anonymous mbufs are mbufs created by sendfile(2) which borrow the pages of the underlying file, either via M_EXTPG or EXT_SFBUF that are sent over a loopback connection. For a well-formed loopback TLS session, the sender should only use sendfile(2) if KTLS is enabled. If TLS is fully handled in userspace, the sender must use write(2) or send(2) which allocate anonymous mbufs. If KTLS transmit is enabled, then sendfile(2) on a loopback connection will always use crypto via OCF and will allocate anonymous pages to hold the encrypted data. However, if sendfile(2) is used to send file-backed data directly over a loopback connection where KTLS is not enabled on the sender side, the KTLS receive path can modify the file-backed pages in place overwriting the file's data. One potential fix would be to replace non-anonymous mbufs in a received TLS record with anonymous mbufs (e.g. via m_dup()) before passing the record to OCF. However, there is no legitimate use case for using sendfile(2) over a loopback TLS connection without using KTLS on the sender side, so instead simply fail decryption requests and close the connection if non-anonymous mbufs are encountered in the RX decryption path. Add a test for this that verifies that the original data backing the file descriptor used as the source for sendfile() is unchanged after being processed. Approved by: so Security: FreeBSD-SA-26:26.ktls Security: CVE-2026-45257 Co-authored-by: Drew Gallatin Sponsored by: Chelsio Communications Sponsored by: Netflix --- sys/kern/uipc_ktls.c | 17 +++++++-- sys/sys/ktls.h | 1 + tests/sys/kern/ktls_test.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c index 61d1a171c467..155c2e6e54f7 100644 --- a/sys/kern/uipc_ktls.c +++ b/sys/kern/uipc_ktls.c @@ -2396,8 +2396,10 @@ tls13_find_record_type(struct ktls_session *tls, struct mbuf *m, int tls_len, * Check if a mbuf chain is fully decrypted at the given offset and * length. Returns KTLS_MBUF_CRYPTO_ST_DECRYPTED if all data is * decrypted. KTLS_MBUF_CRYPTO_ST_MIXED if there is a mix of encrypted - * and decrypted data. Else KTLS_MBUF_CRYPTO_ST_ENCRYPTED if all data - * is encrypted. + * and decrypted data. KTLS_MBUF_CRYPTO_ST_ENCRYPTED if all data is + * encrypted. KTLS_MBUF_CRYPTO_ST_SHAREDMBUF if any mbuf points at + * shared data that must not be modified in place (non-anonymous + * M_EXTPG or sendfile M_EXT buffers). */ ktls_mbuf_crypto_st_t ktls_mbuf_crypto_state(struct mbuf *mb, int offset, int len) @@ -2413,6 +2415,13 @@ ktls_mbuf_crypto_state(struct mbuf *mb, int offset, int len) offset += len; for (; mb != NULL; mb = mb->m_next) { + if ((mb->m_flags & M_EXTPG) != 0 && + (mb->m_epg_flags & EPG_FLAG_ANON) == 0) + return (KTLS_MBUF_CRYPTO_ST_SHAREDMBUF); + if ((mb->m_flags & M_EXT) != 0 && + mb->m_ext.ext_type == EXT_SFBUF) + return (KTLS_MBUF_CRYPTO_ST_SHAREDMBUF); + m_flags_ored |= mb->m_flags; m_flags_anded &= mb->m_flags; @@ -2605,9 +2614,11 @@ ktls_decrypt(struct socket *so) record_type = hdr->tls_type; } break; - default: + case KTLS_MBUF_CRYPTO_ST_SHAREDMBUF: error = EINVAL; break; + default: + __assert_unreachable(); } if (error) { counter_u64_add(ktls_offload_failed_crypto, 1); diff --git a/sys/sys/ktls.h b/sys/sys/ktls.h index 3e3f0b77e4a2..8e5950b8e2b4 100644 --- a/sys/sys/ktls.h +++ b/sys/sys/ktls.h @@ -242,6 +242,7 @@ typedef enum { KTLS_MBUF_CRYPTO_ST_MIXED = 0, KTLS_MBUF_CRYPTO_ST_ENCRYPTED = 1, KTLS_MBUF_CRYPTO_ST_DECRYPTED = -1, + KTLS_MBUF_CRYPTO_ST_SHAREDMBUF = -2, } ktls_mbuf_crypto_st_t; void ktls_check_rx(struct sockbuf *sb); diff --git a/tests/sys/kern/ktls_test.c b/tests/sys/kern/ktls_test.c index 72497196b945..3970083e7f72 100644 --- a/tests/sys/kern/ktls_test.c +++ b/tests/sys/kern/ktls_test.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -2817,6 +2818,97 @@ ATF_TC_BODY(ktls_listening_socket, tc) ATF_REQUIRE(close(s) == 0); } +/* + * Verify that the KTLS receive path does not overwrite data belonging + * to a file whose payload is transmitted over a loopback connection + * via plain sendfile. + */ +ATF_TC_WITHOUT_HEAD(ktls_receive_loopback_sendfile); +ATF_TC_BODY(ktls_receive_loopback_sendfile, tc) +{ + struct tls_enable en; + struct msghdr msg; + struct sf_hdtr hdtr; + struct iovec iov[2]; + uint64_t seqno; + off_t sbytes; + char cbuf[CMSG_SPACE(sizeof(struct tls_get_record))]; + char *plaintext, *ciphertext, *outbuf; + void *p; + const size_t payload_len = PAGE_SIZE; + ssize_t rv; + size_t len; + int mode, shm, sockets[2]; + socklen_t slen; + + ATF_REQUIRE_KTLS(); + seqno = random(); + build_tls_enable(tc, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, + TLS_MINOR_VER_TWO, seqno, &en); + + len = tls_header_len(&en) + payload_len + tls_trailer_len(&en); + plaintext = alloc_buffer(payload_len); + ciphertext = malloc(len); + ATF_REQUIRE_INTEQ(len, encrypt_tls_record(tc, &en, TLS_RLTYPE_APP, + seqno, plaintext, payload_len, ciphertext, len, 0)); + + ATF_REQUIRE((shm = shm_open(SHM_ANON, O_RDWR, 0600)) > 0); + ATF_REQUIRE_INTEQ(0, ftruncate(shm, payload_len)); + ATF_REQUIRE((p = mmap(NULL, payload_len, PROT_READ | PROT_WRITE, + MAP_SHARED, shm, 0)) != MAP_FAILED); + memcpy(p, ciphertext + tls_header_len(&en), payload_len); + + ATF_REQUIRE_MSG(socketpair_tcp(sockets), "failed to create sockets"); + ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, &en, + sizeof(en)) == 0); + slen = sizeof(mode); + ATF_REQUIRE_INTEQ(0, getsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_MODE, + &mode, &slen)); + ATF_REQUIRE_INTEQ(TCP_TLS_MODE_SW, mode); + + fd_set_blocking(sockets[0]); + fd_set_blocking(sockets[1]); + + iov[0].iov_base = ciphertext; + iov[0].iov_len = tls_header_len(&en); + iov[1].iov_base = ciphertext + tls_header_len(&en) + payload_len; + iov[1].iov_len = tls_trailer_len(&en); + hdtr.headers = iov; + hdtr.hdr_cnt = 1; + hdtr.trailers = iov + 1; + hdtr.trl_cnt = 1; + debug_hexdump(tc, p, payload_len, "shm buffer before"); + ATF_REQUIRE_INTEQ(0, sendfile(shm, sockets[1], 0, payload_len, &hdtr, + &sbytes, 0)); + ATF_REQUIRE_INTEQ(sbytes, len); + + outbuf = calloc(payload_len, 1); + + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + iov[0].iov_base = outbuf; + iov[0].iov_len = payload_len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + rv = recvmsg(sockets[0], &msg, 0); + if (rv >= 0) { + ATF_REQUIRE_INTEQ(payload_len, rv); + ATF_REQUIRE_INTEQ(0, memcmp(outbuf, plaintext, payload_len)); + } else + ATF_REQUIRE_ERRNO(EBADMSG, true); + + debug_hexdump(tc, p, payload_len, "shm buffer after"); + ATF_REQUIRE_INTEQ(0, memcmp(p, ciphertext + tls_header_len(&en), + payload_len)); + + close_sockets_ignore_errors(sockets); + (void)close(shm); +} + ATF_TP_ADD_TCS(tp) { /* Transmit tests */ @@ -2843,6 +2935,7 @@ ATF_TP_ADD_TCS(tp) /* Miscellaneous */ ATF_TP_ADD_TC(tp, ktls_sendto_baddst); ATF_TP_ADD_TC(tp, ktls_listening_socket); + ATF_TP_ADD_TC(tp, ktls_receive_loopback_sendfile); return (atf_no_error()); }