aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/kern
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2026-06-03 23:22:00 +0000
committerMark Johnston <markj@FreeBSD.org>2026-06-09 19:13:21 +0000
commit3444414cb4639ef2028abd9b46641e76eadf363d (patch)
treed9deefabc781a136f5a511066818261160c9fb4f /tests/sys/kern
parentbf1e2c07977d6b987f7a976bb9e5b6bdd1ad3986 (diff)
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 <gallatin@FreeBSD.org> Sponsored by: Chelsio Communications Sponsored by: Netflix
Diffstat (limited to 'tests/sys/kern')
-rw-r--r--tests/sys/kern/ktls_test.c93
1 files changed, 93 insertions, 0 deletions
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 <sys/endian.h>
#include <sys/event.h>
#include <sys/ktls.h>
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
@@ -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());
}