From nobody Tue Jun 9 19:19:43 2026 X-Original-To: dev-commits-src-branches@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 4gZdyS512Fz6gVDh for ; Tue, 09 Jun 2026 19:19:44 +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 4gZdyR42spz3QTB for ; Tue, 09 Jun 2026 19:19:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032783; 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=Qf6piFB4Y+sW89fjkl9fywA4ksKsB3uiW+7NmEA9ISA=; b=KSOh1L4qKMXriRH9nAJ7QaBj1D+U+g9xKKRy5MZxIBX0OlY2lwRydkGA2I42uky57oYg4s jReCpQkzX3yDnuoCC3GGFzyupkt6vx1YDIQlvcDtIpqmxBYHD1DX7pCAHsQLo/H9H75iVQ JPUnrfKYDanUE9Sm4P1nycwL3Ze0yV493JnbvWS+EWjD2VyD8ijVz0cNZ0RP6Ki5phjYC+ YydbraXk6yTLdVesZze7yYp4oLy8DOiaj/m4cohKG018Wqbkbn/6Pe7cPYhv92r0N2bleR 2AGT2a0s1t+6FCead8zj1jy4GAV6XN2/nx9ZLwNgpOdDjor1UGpIFAy7wnQsCg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781032783; a=rsa-sha256; cv=none; b=xiNROumfcLF4HB1QkT+dGnrNEX/AuvJrH37KtLbNHknnwmxUfhyGYWnK0F+XEKrCf3kdqQ G2CNb5ufq/4vFCePigI6oUjUnVNZbTODqm/0nTQ18NxtDhbb3q1+TpIjZXduNajF/s2Jnr ETFTsDuD/PzauxzrEeS8Doet2IW/DT3Kt+mDin59Bhhp8nz+Kh6PS1oi09gyWgZB6nnRPa h60fHhGCLDnBJJpi4G39b8ZpSBFCZXQXgj9xGJnJCkNGqC/YY0EaWSOUnvlL7ntBzMm2sZ 5sijDFvv8sqTKgubfE9ZjGH1O7KxljkdiJbSAIjK5rPLNOwcoZXh9MexfHN2Ng== 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=1781032783; 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=Qf6piFB4Y+sW89fjkl9fywA4ksKsB3uiW+7NmEA9ISA=; b=OpWWpdEL3/zXElN1DQcq3iGGLg6BvJloVBeXLGyzTs2Y5S0ZEMRqBX0WB+5Z3TlGD+t8LF k/L08+CSFYiwDFFB/hAGV1Y7B01RmxTFFCiOo3I8xffU5XIN8+ZvlTKEkaCigrd2ouwxt6 mDlj4Ptni4rESgR/Mc72tJpBQ8Bxw3sMdIIzL1451g0XXuvNvjBtRhSU5zgq6AChfLPe56 L3K1DopDSsquFOFHe+Z64OH09wkdPwso7CPZa7/zF015DDu/RvOSO9IPk2FvFYlJer1xax HwMAK1RG2yOFh9FnCNOoGKeJMd/nyRIAu+rDvlYj9dyOmo/T5hJMZJK3ihfXgA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gZdyR3f3gzp1b for ; Tue, 09 Jun 2026 19:19:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3d544 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 09 Jun 2026 19:19:43 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: John Baldwin From: Mark Johnston Subject: git: 540a315cdb46 - releng/15.0 - ktls: Don't attempt to modify non-anonymous mbufs on the receive path List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@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/releng/15.0 X-Git-Reftype: branch X-Git-Commit: 540a315cdb46d6aa3cdbb3797710db652b3c4f4a Auto-Submitted: auto-generated Date: Tue, 09 Jun 2026 19:19:43 +0000 Message-Id: <6a28674f.3d544.633ffb3c@gitrepo.freebsd.org> The branch releng/15.0 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=540a315cdb46d6aa3cdbb3797710db652b3c4f4a commit 540a315cdb46d6aa3cdbb3797710db652b3c4f4a Author: John Baldwin AuthorDate: 2026-06-03 23:22:00 +0000 Commit: Mark Johnston CommitDate: 2026-06-08 15:39:32 +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 66ce1b5a081d..2bc56d166a18 100644 --- a/sys/kern/uipc_ktls.c +++ b/sys/kern/uipc_ktls.c @@ -2416,8 +2416,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) @@ -2433,6 +2435,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; @@ -2633,9 +2642,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 a940bcfaba25..b856dbe8acae 100644 --- a/sys/sys/ktls.h +++ b/sys/sys/ktls.h @@ -238,6 +238,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()); }