* [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP
@ 2025-08-07 23:29 Jakub Kicinski
2025-08-07 23:29 ` [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket Jakub Kicinski
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Jakub Kicinski @ 2025-08-07 23:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy,
Jakub Kicinski
TLS expects that it owns the receive queue of the TCP socket.
This cannot be guaranteed in case the reader of the TCP socket
entered before the TLS ULP was installed, or uses some non-standard
read API (eg. zerocopy ones). Replace the WARN_ON() and a buggy
early exit (which leaves anchor pointing to a freed skb) with real
error handling. Wipe the parsing state and tell the reader to retry.
We already reload the anchor every time we (re)acquire the socket lock,
so the only condition we need to avoid is an out of bounds read
(not having enough bytes in the socket for previously parsed record len).
If some data was read from under TLS but there's enough in the queue
we'll reload and decrypt what is most likely not a valid TLS record.
Leading to some undefined behavior from TLS perspective (corrupting
a stream? missing an alert? missing an attack?) but no kernel crash
should take place.
Reported-by: William Liu <will@willsroot.io>
Reported-by: Savino Dicanosa <savy@syst3mfailure.io>
Link: https://lore.kernel.org/tFjq_kf7sWIG3A7CrCg_egb8CVsT_gsmHAK0_wxDPJXfIzxFAMxqmLwp3MlU5EHiet0AwwJldaaFdgyHpeIUCS-3m3llsmRzp9xIOBR4lAI=@syst3mfailure.io
Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
- fix the reporter tags
- drop the copied_seq nonsense, just correct the error handling
v1: https://lore.kernel.org/20250806180510.3656677-1-kuba@kernel.org
---
net/tls/tls.h | 2 +-
net/tls/tls_strp.c | 11 ++++++++---
net/tls/tls_sw.c | 3 ++-
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h
index 774859b63f0d..4e077068e6d9 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -196,7 +196,7 @@ void tls_strp_msg_done(struct tls_strparser *strp);
int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb);
void tls_rx_msg_ready(struct tls_strparser *strp);
-void tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh);
+bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh);
int tls_strp_msg_cow(struct tls_sw_context_rx *ctx);
struct sk_buff *tls_strp_msg_detach(struct tls_sw_context_rx *ctx);
int tls_strp_msg_hold(struct tls_strparser *strp, struct sk_buff_head *dst);
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index 095cf31bae0b..d71643b494a1 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -475,7 +475,7 @@ static void tls_strp_load_anchor_with_queue(struct tls_strparser *strp, int len)
strp->stm.offset = offset;
}
-void tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
+bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
{
struct strp_msg *rxm;
struct tls_msg *tlm;
@@ -484,8 +484,11 @@ void tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
DEBUG_NET_WARN_ON_ONCE(!strp->stm.full_len);
if (!strp->copy_mode && force_refresh) {
- if (WARN_ON(tcp_inq(strp->sk) < strp->stm.full_len))
- return;
+ if (unlikely(tcp_inq(strp->sk) < strp->stm.full_len)) {
+ WRITE_ONCE(strp->msg_ready, 0);
+ memset(&strp->stm, 0, sizeof(strp->stm));
+ return false;
+ }
tls_strp_load_anchor_with_queue(strp, strp->stm.full_len);
}
@@ -495,6 +498,8 @@ void tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
rxm->offset = strp->stm.offset;
tlm = tls_msg(strp->anchor);
tlm->control = strp->mark;
+
+ return true;
}
/* Called with lock held on lower socket */
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 549d1ea01a72..51c98a007dda 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1384,7 +1384,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
return sock_intr_errno(timeo);
}
- tls_strp_msg_load(&ctx->strp, released);
+ if (unlikely(!tls_strp_msg_load(&ctx->strp, released)))
+ return tls_rx_rec_wait(sk, psock, nonblock, false);
return 1;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket
2025-08-07 23:29 [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Jakub Kicinski
@ 2025-08-07 23:29 ` Jakub Kicinski
2025-08-08 14:29 ` Eric Dumazet
2025-08-08 14:19 ` [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Eric Dumazet
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Jakub Kicinski @ 2025-08-07 23:29 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy,
Jakub Kicinski
Check a race where data disappears from the TCP socket after
TLS signaled that its ready to receive.
ok 6 global.data_steal
# RUN tls_basic.base_base ...
# OK tls_basic.base_base
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/testing/selftests/net/tls.c | 63 +++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 5ded3b3a7538..d8cfcf9bb825 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -2708,6 +2708,69 @@ TEST(prequeue) {
close(cfd);
}
+TEST(data_steal) {
+ struct tls_crypto_info_keys tls;
+ char buf[20000], buf2[20000];
+ struct sockaddr_in addr;
+ int sfd, cfd, ret, fd;
+ int pid, status;
+ socklen_t len;
+
+ len = sizeof(addr);
+ memrnd(buf, sizeof(buf));
+
+ tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_256, &tls, 0);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = 0;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ sfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ ASSERT_EQ(bind(sfd, &addr, sizeof(addr)), 0);
+ ASSERT_EQ(listen(sfd, 10), 0);
+ ASSERT_EQ(getsockname(sfd, &addr, &len), 0);
+ ASSERT_EQ(connect(fd, &addr, sizeof(addr)), 0);
+ ASSERT_GE(cfd = accept(sfd, &addr, &len), 0);
+ close(sfd);
+
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ if (ret) {
+ ASSERT_EQ(errno, ENOENT);
+ SKIP(return, "no TLS support");
+ }
+ ASSERT_EQ(setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")), 0);
+
+ /* Spawn a child and get it into the read wait path of the underlying
+ * TCP socket.
+ */
+ pid = fork();
+ ASSERT_GE(pid, 0);
+ if (!pid) {
+ EXPECT_EQ(recv(cfd, buf, sizeof(buf), MSG_WAITALL),
+ sizeof(buf));
+ exit(!__test_passed(_metadata));
+ }
+
+ usleep(2000);
+ ASSERT_EQ(setsockopt(fd, SOL_TLS, TLS_TX, &tls, tls.len), 0);
+ ASSERT_EQ(setsockopt(cfd, SOL_TLS, TLS_RX, &tls, tls.len), 0);
+
+ EXPECT_EQ(send(fd, buf, sizeof(buf), 0), sizeof(buf));
+ usleep(2000);
+ EXPECT_EQ(recv(cfd, buf2, sizeof(buf2), MSG_DONTWAIT), -1);
+ /* Don't check errno, the error will be different depending
+ * on what random bytes TLS interpreted as the record length.
+ */
+
+ close(fd);
+ close(cfd);
+
+ EXPECT_EQ(wait(&status), pid);
+ EXPECT_EQ(status, 0);
+}
+
static void __attribute__((constructor)) fips_check(void) {
int res;
FILE *f;
--
2.50.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP
2025-08-07 23:29 [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Jakub Kicinski
2025-08-07 23:29 ` [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket Jakub Kicinski
@ 2025-08-08 14:19 ` Eric Dumazet
2025-08-12 10:45 ` Paolo Abeni
2025-08-13 3:01 ` patchwork-bot+netdevbpf
3 siblings, 0 replies; 8+ messages in thread
From: Eric Dumazet @ 2025-08-08 14:19 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, pabeni, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy
On Thu, Aug 7, 2025 at 4:29 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> TLS expects that it owns the receive queue of the TCP socket.
> This cannot be guaranteed in case the reader of the TCP socket
> entered before the TLS ULP was installed, or uses some non-standard
> read API (eg. zerocopy ones). Replace the WARN_ON() and a buggy
> early exit (which leaves anchor pointing to a freed skb) with real
> error handling. Wipe the parsing state and tell the reader to retry.
>
> We already reload the anchor every time we (re)acquire the socket lock,
> so the only condition we need to avoid is an out of bounds read
> (not having enough bytes in the socket for previously parsed record len).
>
> If some data was read from under TLS but there's enough in the queue
> we'll reload and decrypt what is most likely not a valid TLS record.
> Leading to some undefined behavior from TLS perspective (corrupting
> a stream? missing an alert? missing an attack?) but no kernel crash
> should take place.
>
> Reported-by: William Liu <will@willsroot.io>
> Reported-by: Savino Dicanosa <savy@syst3mfailure.io>
> Link: https://lore.kernel.org/tFjq_kf7sWIG3A7CrCg_egb8CVsT_gsmHAK0_wxDPJXfIzxFAMxqmLwp3MlU5EHiet0AwwJldaaFdgyHpeIUCS-3m3llsmRzp9xIOBR4lAI=@syst3mfailure.io
> Fixes: 84c61fe1a75b ("tls: rx: do not use the standard strparser")
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> v2:
> - fix the reporter tags
> - drop the copied_seq nonsense, just correct the error handling
> v1: https://lore.kernel.org/20250806180510.3656677-1-kuba@kernel.org
> ---
Reviewed-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket
2025-08-07 23:29 ` [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket Jakub Kicinski
@ 2025-08-08 14:29 ` Eric Dumazet
0 siblings, 0 replies; 8+ messages in thread
From: Eric Dumazet @ 2025-08-08 14:29 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, pabeni, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy
On Thu, Aug 7, 2025 at 4:29 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> Check a race where data disappears from the TCP socket after
> TLS signaled that its ready to receive.
>
> ok 6 global.data_steal
> # RUN tls_basic.base_base ...
> # OK tls_basic.base_base
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
Reviewed-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP
2025-08-07 23:29 [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Jakub Kicinski
2025-08-07 23:29 ` [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket Jakub Kicinski
2025-08-08 14:19 ` [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Eric Dumazet
@ 2025-08-12 10:45 ` Paolo Abeni
2025-08-12 13:28 ` Jakub Kicinski
2025-08-13 3:01 ` patchwork-bot+netdevbpf
3 siblings, 1 reply; 8+ messages in thread
From: Paolo Abeni @ 2025-08-12 10:45 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, andrew+netdev, horms, borisp, john.fastabend,
shuah, linux-kselftest, sd, will, savy
On 8/8/25 1:29 AM, Jakub Kicinski wrote:
> diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
> index 549d1ea01a72..51c98a007dda 100644
> --- a/net/tls/tls_sw.c
> +++ b/net/tls/tls_sw.c
> @@ -1384,7 +1384,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
> return sock_intr_errno(timeo);
> }
>
> - tls_strp_msg_load(&ctx->strp, released);
> + if (unlikely(!tls_strp_msg_load(&ctx->strp, released)))
> + return tls_rx_rec_wait(sk, psock, nonblock, false);
I'm probably missing something relevant, but I don't see anything
preventing the above recursion from going very deep and cause stack
overflow.
Perhaps something alike:
released = false;
goto <function start>
would be safer?
/P
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP
2025-08-12 10:45 ` Paolo Abeni
@ 2025-08-12 13:28 ` Jakub Kicinski
2025-08-12 16:23 ` Paolo Abeni
0 siblings, 1 reply; 8+ messages in thread
From: Jakub Kicinski @ 2025-08-12 13:28 UTC (permalink / raw)
To: Paolo Abeni
Cc: davem, netdev, edumazet, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy
On Tue, 12 Aug 2025 12:45:56 +0200 Paolo Abeni wrote:
> On 8/8/25 1:29 AM, Jakub Kicinski wrote:
> > diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
> > index 549d1ea01a72..51c98a007dda 100644
> > --- a/net/tls/tls_sw.c
> > +++ b/net/tls/tls_sw.c
> > @@ -1384,7 +1384,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
> > return sock_intr_errno(timeo);
> > }
> >
> > - tls_strp_msg_load(&ctx->strp, released);
> > + if (unlikely(!tls_strp_msg_load(&ctx->strp, released)))
> > + return tls_rx_rec_wait(sk, psock, nonblock, false);
>
> I'm probably missing something relevant, but I don't see anything
> preventing the above recursion from going very deep and cause stack
> overflow.
>
> Perhaps something alike:
>
> released = false;
> goto <function start>
>
> would be safer?
It's a tail call to the same function, the compiler should do that for
us automatically. Can we not trust the compiler to be sensible? Both
clang and gcc get it right.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP
2025-08-12 13:28 ` Jakub Kicinski
@ 2025-08-12 16:23 ` Paolo Abeni
0 siblings, 0 replies; 8+ messages in thread
From: Paolo Abeni @ 2025-08-12 16:23 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy
On 8/12/25 3:28 PM, Jakub Kicinski wrote:
> On Tue, 12 Aug 2025 12:45:56 +0200 Paolo Abeni wrote:
>> On 8/8/25 1:29 AM, Jakub Kicinski wrote:
>>> diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
>>> index 549d1ea01a72..51c98a007dda 100644
>>> --- a/net/tls/tls_sw.c
>>> +++ b/net/tls/tls_sw.c
>>> @@ -1384,7 +1384,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
>>> return sock_intr_errno(timeo);
>>> }
>>>
>>> - tls_strp_msg_load(&ctx->strp, released);
>>> + if (unlikely(!tls_strp_msg_load(&ctx->strp, released)))
>>> + return tls_rx_rec_wait(sk, psock, nonblock, false);
>>
>> I'm probably missing something relevant, but I don't see anything
>> preventing the above recursion from going very deep and cause stack
>> overflow.
>>
>> Perhaps something alike:
>>
>> released = false;
>> goto <function start>
>>
>> would be safer?
>
> It's a tail call to the same function, the compiler should do that for
> us automatically. Can we not trust the compiler to be sensible? Both
> clang and gcc get it right.
Sound reasonable, I dumbly did not consider it. I'm fine with the patch
in the current form.
/P
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP
2025-08-07 23:29 [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Jakub Kicinski
` (2 preceding siblings ...)
2025-08-12 10:45 ` Paolo Abeni
@ 2025-08-13 3:01 ` patchwork-bot+netdevbpf
3 siblings, 0 replies; 8+ messages in thread
From: patchwork-bot+netdevbpf @ 2025-08-13 3:01 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, borisp,
john.fastabend, shuah, linux-kselftest, sd, will, savy
Hello:
This series was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Thu, 7 Aug 2025 16:29:06 -0700 you wrote:
> TLS expects that it owns the receive queue of the TCP socket.
> This cannot be guaranteed in case the reader of the TCP socket
> entered before the TLS ULP was installed, or uses some non-standard
> read API (eg. zerocopy ones). Replace the WARN_ON() and a buggy
> early exit (which leaves anchor pointing to a freed skb) with real
> error handling. Wipe the parsing state and tell the reader to retry.
>
> [...]
Here is the summary with links:
- [net,v2,1/2] tls: handle data disappearing from under the TLS ULP
https://git.kernel.org/netdev/net/c/6db015fc4b5d
- [net,v2,2/2] selftests: tls: test TCP stealing data from under the TLS socket
https://git.kernel.org/netdev/net/c/d7e82594a45c
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-08-13 3:01 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-07 23:29 [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Jakub Kicinski
2025-08-07 23:29 ` [PATCH net v2 2/2] selftests: tls: test TCP stealing data from under the TLS socket Jakub Kicinski
2025-08-08 14:29 ` Eric Dumazet
2025-08-08 14:19 ` [PATCH net v2 1/2] tls: handle data disappearing from under the TLS ULP Eric Dumazet
2025-08-12 10:45 ` Paolo Abeni
2025-08-12 13:28 ` Jakub Kicinski
2025-08-12 16:23 ` Paolo Abeni
2025-08-13 3:01 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).