Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH net 0/2] tls: Honor O_NONBLOCK in tls_sw_splice_read()
@ 2026-10-27  1:52 Jakub Kicinski
  2026-10-27  1:52 ` [PATCH net 1/2] " Jakub Kicinski
  2026-10-27  1:52 ` [PATCH net 2/2] selftests: tls: Cover splice on a nonblocking socket Jakub Kicinski
  0 siblings, 2 replies; 3+ messages in thread
From: Jakub Kicinski @ 2026-10-27  1:52 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, sd,
	john.fastabend, shuah, cel, linux-kselftest, Jakub Kicinski

Extract the O_NONBLOCK fix from Chuck's zero-len record series:
https://lore.kernel.org/20261019-tls-follow-on-v6-0-ab01c4ecca0c@kernel.org

The first patch carries the fix. LLMs love to point out the missing
O_NONBLOCK as a risk for the zero-len record change so let's get
it out of the way before working on the zero-len record fix which
is more complex.

The fix itself is almost unchange (I deleted some parens),
the selftest was fixed up a little bit to appease the LLM
reviewers but also no substantial change.

Chuck Lever (2):
  tls: Honor O_NONBLOCK in tls_sw_splice_read()
  selftests: tls: Cover splice on a nonblocking socket

 net/tls/tls_sw.c                  |  9 ++++++---
 tools/testing/selftests/net/tls.c | 31 +++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 3 deletions(-)

-- 
2.56.0


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

* [PATCH net 1/2] tls: Honor O_NONBLOCK in tls_sw_splice_read()
  2026-10-27  1:52 [PATCH net 0/2] tls: Honor O_NONBLOCK in tls_sw_splice_read() Jakub Kicinski
@ 2026-10-27  1:52 ` Jakub Kicinski
  2026-10-27  1:52 ` [PATCH net 2/2] selftests: tls: Cover splice on a nonblocking socket Jakub Kicinski
  1 sibling, 0 replies; 3+ messages in thread
From: Jakub Kicinski @ 2026-10-27  1:52 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, sd,
	john.fastabend, shuah, cel, linux-kselftest, Jakub Kicinski

From: Chuck Lever <cel@kernel.org>

tls_sw_splice_read() takes its blocking behavior from
SPLICE_F_NONBLOCK alone. When splicing from a socket to a pipe,
do_splice() sets that flag from the pipe's O_NONBLOCK, not the
socket's. On a nonblocking socket, a splice(2) call without
SPLICE_F_NONBLOCK therefore sleeps in tls_rx_rec_wait() until a
record arrives. tcp_splice_read() instead reads sock->file->f_flags
and returns -EAGAIN.

Polling first does not avoid the sleep once tls_sw_splice_read()
consumes zero-length records, as it does after a later patch in this
series. tls_sw_sock_is_readable() reports the socket readable while
any record sits on rx_list, including a zero-length record that
delivers no bytes to the pipe. For TLS 1.3 the strparser announces a
record before decryption, when its plaintext length is still unknown,
so the readiness test cannot screen such a record out. A splice that
consumes the record and waits for the next stalls every connection
an event loop multiplexes.

An LLM audit of the TLS read paths found this defect.

Treat the socket's O_NONBLOCK as nonblocking too. A caller that
sets O_NONBLOCK on the socket and splices without SPLICE_F_NONBLOCK
now gets -EAGAIN where the splice blocked before. sendfile(2) from
a TLS socket changes the same way, because do_sendfile() leaves the
input file's O_NONBLOCK out of the splice flags. Both now behave as
they do on a plain TCP socket.

Fixes: c46234ebb4d1 ("tls: RX path for ktls")
Signed-off-by: Chuck Lever <cel@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 net/tls/tls_sw.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 312e51270f29..771e02bff519 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2007,10 +2007,14 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t *ppos,
 	struct tls_msg *tlm;
 	struct sk_buff *skb;
 	ssize_t copied = 0;
+	bool nonblock;
 	int chunk;
 	int err;
 
-	err = tls_rx_reader_lock(sk, ctx, flags & SPLICE_F_NONBLOCK);
+	nonblock = flags & SPLICE_F_NONBLOCK ||
+		   sock->file->f_flags & O_NONBLOCK;
+
+	err = tls_rx_reader_lock(sk, ctx, nonblock);
 	if (err < 0)
 		return err;
 
@@ -2024,8 +2028,7 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t *ppos,
 	} else {
 		struct tls_decrypt_arg darg;
 
-		err = tls_rx_rec_wait(sk, flags & SPLICE_F_NONBLOCK,
-				      true, false);
+		err = tls_rx_rec_wait(sk, nonblock, true, false);
 		if (err <= 0)
 			goto splice_read_end;
 
-- 
2.56.0


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

* [PATCH net 2/2] selftests: tls: Cover splice on a nonblocking socket
  2026-10-27  1:52 [PATCH net 0/2] tls: Honor O_NONBLOCK in tls_sw_splice_read() Jakub Kicinski
  2026-10-27  1:52 ` [PATCH net 1/2] " Jakub Kicinski
@ 2026-10-27  1:52 ` Jakub Kicinski
  1 sibling, 0 replies; 3+ messages in thread
From: Jakub Kicinski @ 2026-10-27  1:52 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, sd,
	john.fastabend, shuah, cel, linux-kselftest, Jakub Kicinski

From: Chuck Lever <cel@kernel.org>

Every existing splice call in this file either passes
SPLICE_F_NONBLOCK or runs on a blocking socket, so nothing
exercises the socket's own O_NONBLOCK on the splice path. A change
that stops consulting sock->file->f_flags puts splice(2) and
sendfile(2) back to sleeping in tls_rx_rec_wait(), and the suite
still reports pass.

Splice from a nonblocking socket without SPLICE_F_NONBLOCK, first
with no record queued, which must return EAGAIN, then with a record
to deliver. A kernel that ignores the socket flag sleeps until the
harness timeout rather than returning.

Signed-off-by: Chuck Lever <cel@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 tools/testing/selftests/net/tls.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 9d3cd4fff062..c4203401c4bf 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -889,6 +889,37 @@ TEST_F(tls, splice_to_pipe)
 	EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
 }
 
+/* The socket's O_NONBLOCK governs the record wait, as on a plain TCP
+ * socket, even without SPLICE_F_NONBLOCK.
+ */
+TEST_F(tls, splice_nonblock_socket)
+{
+	struct pollfd pfd = { .fd = self->cfd, .events = POLLIN };
+	int send_len = TLS_PAYLOAD_MAX_LEN;
+	char mem_send[TLS_PAYLOAD_MAX_LEN];
+	char mem_recv[TLS_PAYLOAD_MAX_LEN];
+	int flags, p[2];
+
+	flags = fcntl(self->cfd, F_GETFL, 0);
+	ASSERT_GE(flags, 0);
+	ASSERT_EQ(fcntl(self->cfd, F_SETFL, flags | O_NONBLOCK), 0);
+
+	memrnd(mem_send, sizeof(mem_send));
+
+	ASSERT_GE(pipe(p), 0);
+	EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+	EXPECT_EQ(errno, EAGAIN);
+
+	/* The record may still be in flight, wait for it. A failed splice()
+	 * would leave the read() below blocking until the harness timeout.
+	 */
+	EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+	ASSERT_EQ(poll(&pfd, 1, 5000), 1);
+	ASSERT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+	EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+	EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+}
+
 TEST_F(tls, splice_cmsg_to_pipe)
 {
 	char *test_str = "test_read";
-- 
2.56.0


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

end of thread, other threads:[~2026-10-27  1:52 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27  1:52 [PATCH net 0/2] tls: Honor O_NONBLOCK in tls_sw_splice_read() Jakub Kicinski
2026-10-27  1:52 ` [PATCH net 1/2] " Jakub Kicinski
2026-10-27  1:52 ` [PATCH net 2/2] selftests: tls: Cover splice on a nonblocking socket Jakub Kicinski

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