From: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr> mainline inclusion from mainline-v7.2-rc7 commit 1c8629651cb54f7b51db8fc0b1a9944e4a4b0f5e category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18180 CVE: CVE-2026-74611 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- tls_decrypt_sg() advances msg->msg_iter when it maps user pages for the optimistic TLS 1.3 zero-copy path. If the decrypted record turns out not to be unpadded application data, tls_decrypt_sw() retries into a kernel skb, but leaves the iterator advanced. The subsequent copy from the skb then writes decrypted bytes again at a later point in the caller iovecs while recvmsg() reports only the post-retry length. A TLS peer can trigger this after the receiver enables TLS_RX_EXPECT_NO_PAD. Revert the iterator by the number of bytes consumed by the optimistic mapping before retrying without zero-copy. Add a selftest which sends a TLS 1.3 control record with TLS_RX_EXPECT_NO_PAD enabled and verifies that recvmsg() does not overwrite later iovecs beyond the returned length. Fixes: ce61327ce989 ("tls: rx: support optimistic decrypt to user buffer with TLS 1.3") Cc: stable@vger.kernel.org Signed-off-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr> Link: https://patch.msgid.link/20260804125528.2139928-1-Jeremy.Jean@oss.cyber.gouv... Signed-off-by: Jakub Kicinski <kuba@kernel.org> Conflicts: tools/testing/selftests/net/tls.c [commit dc54b813df63 ("selftests: tls: add test with a partially invalid iov"), 555f0edb9ff0 ("selftests: tls: add rekey tests") and b2e584aa3c71 ("selftests: tls: add key_generation argument to tls_crypto_info_init") are not backport, which lead to conflicts] Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com> --- net/tls/tls_sw.c | 2 ++ tools/testing/selftests/net/tls.c | 58 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 934f95bab928..9361e0f84571 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1733,18 +1733,20 @@ tls_decrypt_sw(struct sock *sk, struct tls_context *tls_ctx, if (err == -EBADMSG) TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSDECRYPTERROR); return err; } /* keep going even for ->async, the code below is TLS 1.3 */ /* If opportunistic TLS 1.3 ZC failed retry without ZC */ if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION && darg->tail != TLS_RECORD_TYPE_DATA)) { + iov_iter_revert(&msg->msg_iter, strp_msg(darg->skb)->full_len - + prot->overhead_size); darg->zc = false; if (!darg->tail) TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXNOPADVIOL); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSDECRYPTRETRY); return tls_decrypt_sw(sk, tls_ctx, msg, darg); } pad = tls_padding_length(prot, darg->skb, darg); if (pad < 0) { diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index bc36c91c4480..e0d6cfb825ae 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -2108,18 +2108,76 @@ TEST(prequeue) { ASSERT_EQ(setsockopt(cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0); EXPECT_EQ(recv(cfd, buf2, sizeof(buf2), MSG_WAITALL), sizeof(buf2)); EXPECT_EQ(memcmp(buf, buf2, sizeof(buf)), 0); close(fd); close(cfd); } +#define TLS_RECORD_TYPE_HANDSHAKE 0x16 + +TEST_F(tls_basic, recvmsg_nopad_retry_iov) +{ + char payload[32]; + char first_iov[sizeof(payload)]; + char later_iov[sizeof(payload) * 2]; + char expected_later_iov[sizeof(later_iov)]; + char cbuf[CMSG_SPACE(sizeof(char))]; + struct tls_crypto_info_keys tls13; + struct iovec iov[] = { + { .iov_base = first_iov, .iov_len = sizeof(first_iov) }, + { .iov_base = later_iov, .iov_len = sizeof(later_iov) }, + }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + }; + int one = 1; + int ret; + int i; + + if (self->notls) + SKIP(return, "no TLS support"); + + tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128, + &tls13); + + ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls13, tls13.len); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls13, tls13.len); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD, + &one, sizeof(one)); + ASSERT_EQ(ret, 0); + + for (i = 0; i < sizeof(payload); i++) + payload[i] = 0x40 + i; + memset(first_iov, 0xa5, sizeof(first_iov)); + memset(later_iov, 0x5a, sizeof(later_iov)); + memset(expected_later_iov, 0x5a, sizeof(expected_later_iov)); + + /* A control record forces optimistic TLS 1.3 RX to retry. */ + ret = tls_send_cmsg(self->fd, TLS_RECORD_TYPE_HANDSHAKE, + payload, sizeof(payload), 0); + ASSERT_EQ(ret, sizeof(payload)); + + ret = recvmsg(self->cfd, &msg, 0); + ASSERT_EQ(ret, sizeof(payload)); + EXPECT_EQ(memcmp(first_iov, payload, sizeof(payload)), 0); + EXPECT_EQ(memcmp(later_iov, expected_later_iov, + sizeof(later_iov)), 0); +} + static void __attribute__((constructor)) fips_check(void) { int res; FILE *f; f = fopen("/proc/sys/crypto/fips_enabled", "r"); if (f) { res = fscanf(f, "%d", &fips_enabled); if (res != 1) ksft_print_msg("ERROR: Couldn't read /proc/sys/crypto/fips_enabled\n"); -- 2.43.0