Netdev List
 help / color / mirror / Atom feed
* [PATCH net 0/2] net/tls: Fail splice after a failed async decrypt
@ 2026-08-07  0:44 Chuck Lever
  2026-08-07  0:44 ` [PATCH net 1/2] net/tls: Fail tls_sw_splice_read() " Chuck Lever
  2026-08-07  0:44 ` [PATCH net 2/2] selftests: tls: cover splice after a failed decrypt Chuck Lever
  0 siblings, 2 replies; 3+ messages in thread
From: Chuck Lever @ 2026-08-07  0:44 UTC (permalink / raw)
  To: John Fastabend, Jakub Kicinski, Sabrina Dubroca, David S. Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, Shuah Khan
  Cc: netdev, linux-kernel, linux-kselftest, Chuck Lever

tls_sw_recvmsg() and tls_sw_read_sock() both read ctx->async_wait.err
once they hold the reader lock, so a record that failed
authentication fails the call. tls_sw_splice_read() has no such
check. sk_err does not stand in for one. The first reader to reach
sock_error() clears sk_err, while async_wait.err persists. A splice
therefore keeps delivering records on a connection the other two
readers have already refused.

Both patches come from a receive-path series for zero-length data
records. Jakub asked for them separately, since the rest of that
series is still under discussion.

Link to the original series:
https://patch.msgid.link/20260726-tls-follow-on-v1-0-99bf4cc1c729@kernel.org

---
Chuck Lever (2):
      net/tls: Fail tls_sw_splice_read() after a failed async decrypt
      selftests: tls: cover splice after a failed decrypt

 net/tls/tls_sw.c                  |  5 +++
 tools/testing/selftests/net/tls.c | 75 +++++++++++++++++++++++++++++++++------
 2 files changed, 70 insertions(+), 10 deletions(-)
---
base-commit: 594d905195024b228c962627ae5ae7c17bd582a4
change-id: 20260806-tls-splice-crypto-fix-2a6de3cc0224

Best regards,
--  
Chuck Lever <cel@kernel.org>


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH net 1/2] net/tls: Fail tls_sw_splice_read() after a failed async decrypt
  2026-08-07  0:44 [PATCH net 0/2] net/tls: Fail splice after a failed async decrypt Chuck Lever
@ 2026-08-07  0:44 ` Chuck Lever
  2026-08-07  0:44 ` [PATCH net 2/2] selftests: tls: cover splice after a failed decrypt Chuck Lever
  1 sibling, 0 replies; 3+ messages in thread
From: Chuck Lever @ 2026-08-07  0:44 UTC (permalink / raw)
  To: John Fastabend, Jakub Kicinski, Sabrina Dubroca, David S. Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, Shuah Khan
  Cc: netdev, linux-kernel, linux-kselftest, Chuck Lever

When an async decrypt fails, tls_decrypt_done() records the error in
ctx->async_wait.err and calls tls_err_abort(), which stores it in
sk_err. tls_sw_recvmsg() and tls_sw_read_sock() each read
async_wait.err once they hold the reader lock and fail the call: a
record that did not authenticate breaks the connection.

tls_sw_splice_read() has no such check, and sk_err does not stand in
for one. tls_rx_rec_wait() tests sk_err only inside the loop it
skips whenever a record is already parsed, and the first reader to
reach sock_error() clears it, while async_wait.err persists. A
splice therefore keeps delivering records on a connection that
recvmsg() and read_sock() refuse to read.

Read async_wait.err in tls_sw_splice_read() as the other two readers
do.

Fixes: f314bfee81b1 ("tls: rx: return the already-copied data on crypto error")
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Chuck Lever <cel@kernel.org>
---
 net/tls/tls_sw.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 62d46736e24b..d1ad31986cf2 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2014,6 +2014,11 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t *ppos,
 	if (err < 0)
 		return err;
 
+	/* If crypto failed the connection is broken */
+	err = ctx->async_wait.err;
+	if (err)
+		goto splice_read_end;
+
 	if (!skb_queue_empty(&ctx->rx_list)) {
 		skb = __skb_dequeue(&ctx->rx_list);
 	} else {

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH net 2/2] selftests: tls: cover splice after a failed decrypt
  2026-08-07  0:44 [PATCH net 0/2] net/tls: Fail splice after a failed async decrypt Chuck Lever
  2026-08-07  0:44 ` [PATCH net 1/2] net/tls: Fail tls_sw_splice_read() " Chuck Lever
@ 2026-08-07  0:44 ` Chuck Lever
  1 sibling, 0 replies; 3+ messages in thread
From: Chuck Lever @ 2026-08-07  0:44 UTC (permalink / raw)
  To: John Fastabend, Jakub Kicinski, Sabrina Dubroca, David S. Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, Shuah Khan
  Cc: netdev, linux-kernel, linux-kselftest, Chuck Lever

Nothing in this file splices a socket whose last decrypt failed, so
the check that fails tls_sw_splice_read() on a broken connection can
be removed without a test noticing. Such a splice hands the
application plaintext that recvmsg() and read_sock() already refuse
to return.

Extend the bad_auth pattern. Corrupt an authenticated record, confirm
recvmsg() reports EBADMSG, then splice the same socket and require
EBADMSG again. A synchronous decrypt fails again on the still-queued
record, so only an async decrypt reaches EBADMSG through the
recorded-failure check alone.

bad_auth builds the same corrupted record, so its construction moves
into a helper the two tests share.

Signed-off-by: Chuck Lever <cel@kernel.org>
---
 tools/testing/selftests/net/tls.c | 75 +++++++++++++++++++++++++++++++++------
 1 file changed, 65 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index a0e72e402748..9d3cd4fff062 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -24,6 +24,7 @@
 #include "kselftest_harness.h"
 
 #define TLS_PAYLOAD_MAX_LEN 16384
+#define TLS_HDR_LEN 5
 #define SOL_TLS 282
 
 static int fips_enabled;
@@ -2732,28 +2733,83 @@ TEST_F(tls_err, bad_rec)
 	EXPECT_EQ(errno, EAGAIN);
 }
 
+/* cfd carries a byte stream, so one recv() can return part of a
+ * record. Take the fragment length from the record header and wait
+ * for the remainder.
+ */
+static void tls_send_bad_auth(struct __test_metadata *_metadata,
+			      int fd, int cfd, int fd2)
+{
+	char buf[128];
+	int len;
+
+	memrnd(buf, sizeof(buf) / 2);
+	ASSERT_EQ(send(fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2);
+
+	ASSERT_EQ(recv(cfd, buf, TLS_HDR_LEN, MSG_WAITALL), TLS_HDR_LEN);
+
+	len = ((unsigned char)buf[3] << 8) | (unsigned char)buf[4];
+	ASSERT_GT(len, 0);
+	ASSERT_LE(len, (int)sizeof(buf) - TLS_HDR_LEN);
+
+	ASSERT_EQ(recv(cfd, buf + TLS_HDR_LEN, len, MSG_WAITALL), len);
+
+	buf[TLS_HDR_LEN + len - 1]++;
+
+	ASSERT_EQ(send(fd2, buf, TLS_HDR_LEN + len, 0), TLS_HDR_LEN + len);
+}
+
 TEST_F(tls_err, bad_auth)
 {
 	char buf[128];
-	int n;
 
 	if (self->notls)
 		SKIP(return, "no TLS support");
 
-	memrnd(buf, sizeof(buf) / 2);
-	EXPECT_EQ(send(self->fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2);
-	n = recv(self->cfd, buf, sizeof(buf), 0);
-	EXPECT_GT(n, sizeof(buf) / 2);
+	tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2);
 
-	buf[n - 1]++;
-
-	EXPECT_EQ(send(self->fd2, buf, n, 0), n);
 	EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
 	EXPECT_EQ(errno, EBADMSG);
 	EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
 	EXPECT_EQ(errno, EBADMSG);
 }
 
+/* A record that did not authenticate breaks the connection for every
+ * reader, splice included.
+ *
+ * The two decrypt paths reach that result differently. A synchronous
+ * decrypt leaves the record parsed, so the splice re-runs the decrypt
+ * and fails on the record itself; the ctx->async_wait.err check in
+ * tls_sw_splice_read() is not what stops it. Only an asynchronous
+ * decrypt, which needs a TLS 1.2 socket and an AEAD advertising
+ * CRYPTO_ALG_ASYNC, consumes the record before the failure is
+ * recorded, leaving that check the sole reason the splice fails.
+ */
+TEST_F(tls_err, bad_auth_splice)
+{
+	char buf[128];
+	ssize_t ret;
+	int p[2];
+
+	if (self->notls)
+		SKIP(return, "no TLS support");
+
+	tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2);
+
+	EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
+	EXPECT_EQ(errno, EBADMSG);
+
+	ASSERT_GE(pipe(p), 0);
+
+	ret = splice(self->cfd2, NULL, p[1], NULL, sizeof(buf),
+		     SPLICE_F_NONBLOCK);
+	EXPECT_EQ(ret, -1);
+	EXPECT_EQ(errno, EBADMSG);
+
+	close(p[0]);
+	close(p[1]);
+}
+
 TEST_F(tls_err, bad_in_large_read)
 {
 	char txt[3][64];
@@ -3009,7 +3065,6 @@ static size_t parse_tls_records(struct __test_metadata *_metadata,
 {
 	const __u8 *rec = rx_buf;
 	size_t total_plaintext_rx = 0;
-	const __u8 rec_header_len = 5;
 
 	while (rec < rx_buf + rx_len) {
 		__u16 record_payload_len;
@@ -3029,7 +3084,7 @@ static size_t parse_tls_records(struct __test_metadata *_metadata,
 
 		/* Plaintext must not exceed the specified limit */
 		ASSERT_LE(plaintext_len, max_payload_len);
-		rec += rec_header_len + record_payload_len;
+		rec += TLS_HDR_LEN + record_payload_len;
 	}
 
 	return total_plaintext_rx;

-- 
2.54.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-07  0:44 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-07  0:44 [PATCH net 0/2] net/tls: Fail splice after a failed async decrypt Chuck Lever
2026-08-07  0:44 ` [PATCH net 1/2] net/tls: Fail tls_sw_splice_read() " Chuck Lever
2026-08-07  0:44 ` [PATCH net 2/2] selftests: tls: cover splice after a failed decrypt Chuck Lever

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox