[PATCH net 2/2] selftests: tls: add a test for splicing onto a full plaintext record
From: chanyoung <hidden>
Date: 2026-07-26 10:56:56
Also in:
linux-kselftest
Subsystem:
kernel selftest framework, networking [general], networking [tls], the rest · Maintainers:
Shuah Khan, Shuah Khan, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, John Fastabend, Sabrina Dubroca, Linus Torvalds
Splicing into an open record whose plaintext scatterlist ring was already full used to wrap the ring's end index onto its start. sk_msg_full() then reported the full ring as empty, so the loop kept overwriting live entries while sg.size grew, and pushing the record ran the AEAD scatterwalk off the end of the scatterlist. Reaching that state needs the ring to be left full and unpushed across a syscall, which the copy path does when the fragment it adds is the one that fills the ring. The test therefore fills the ring with splices, adds one byte with MSG_MORE, splices some more, and only then pushes the record. MAX_SKB_FRAGS is configurable, so rather than hardcoding the number of fragments needed to fill the ring, sweep it over the plausible range so the one-byte send lands exactly on a full ring whatever the kernel was built with. On an unpatched kernel this oopses in the AEAD walk; with the preceding patch applied the whole tls selftest suite passes. Signed-off-by: Chanyoung Park <redacted> --- tools/testing/selftests/net/tls.c | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index cbdd3ea28b9..d2666884ea8 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c@@ -835,6 +835,58 @@ TEST_F(tls, send_and_splice) EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); } +/* Splicing into an open record whose plaintext scatterlist ring is already + * full used to wrap the ring's end index onto its start, after which + * sk_msg_full() reported the full ring as empty: further pages overwrote + * live entries and sg.size desynced from the walkable scatterlist, which + * oopsed in the AEAD walk once the record was pushed. The ring is left + * full and unpushed by the copy path, which does not push the record when + * the fragment it adds is the one that fills the ring. + */ +TEST_F(tls, splice_onto_full_record) +{ + int frag_len = 100, extra = 4; + char mem_send[5000]; + char mem_recv[5000]; + int nfrags, i, total; + int p[2]; + + memrnd(mem_send, sizeof(mem_send)); + + /* MAX_SKB_FRAGS is configurable (17 by default), so sweep the + * plausible range to land the one-byte send exactly on a full ring + * whatever this kernel was built with. + */ + for (nfrags = 12; nfrags <= 45; nfrags++) { + total = (nfrags + extra) * frag_len + 2; + + for (i = 0; i < nfrags; i++) { + ASSERT_GE(pipe(p), 0); + EXPECT_EQ(write(p[1], mem_send, frag_len), frag_len); + EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, frag_len, + SPLICE_F_MORE), frag_len); + close(p[0]); + close(p[1]); + } + + EXPECT_EQ(send(self->fd, mem_send, 1, MSG_MORE), 1); + + for (i = 0; i < extra; i++) { + ASSERT_GE(pipe(p), 0); + EXPECT_EQ(write(p[1], mem_send, frag_len), frag_len); + EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, frag_len, + SPLICE_F_MORE), frag_len); + close(p[0]); + close(p[1]); + } + + EXPECT_EQ(send(self->fd, mem_send, 1, 0), 1); + + EXPECT_EQ(recv(self->cfd, mem_recv, total, MSG_WAITALL), total); + EXPECT_EQ(memcmp(mem_send, mem_recv, frag_len), 0); + } +} + TEST_F(tls, splice_to_pipe) { int send_len = TLS_PAYLOAD_MAX_LEN;
--
2.43.0