A zero-length application_data record decrypts to full_len == 0,
so tls_sw_splice_read() splices zero bytes and returns zero. A zero
return from a splice read means EOF, and the caller tears down a
connection that is still live.

Consume the record and fetch the next one, as tls_sw_recvmsg()
does. Each fetch restarts SO_RCVTIMEO under the socket lock, so a
stream of empty records could hold a blocking splice and the lock
for as long as the stream lasts. Stop after TLS_RX_NODATA_LIMIT
consecutive empty records and return -EAGAIN. The cap stands in for
the timeout the retry defeats, so a blocking splice with no timeout
set reports -EAGAIN too, as a timed-out receive would. Test for a
pending signal before each retry as well, so the splicing task
stays killable. A nonblocking splice gets -EAGAIN, as
tls_sw_recvmsg() does.

When an empty record precedes a close_notify, splice(2) now returns
-EINVAL from the control-record test instead of a false EOF. A
splice that reaches an alert record already reports -EINVAL.

Fixes: c46234ebb4d1 ("tls: RX path for ktls")
Reported-by: Sabrina Dubroca <[email protected]>
Closes: https://lore.kernel.org/netdev/akaoXcfamBp8_mYe@krikkit/
Signed-off-by: Chuck Lever <[email protected]>
---
 net/tls/tls_sw.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index cb5a2b29bced..53a4af9eeeef 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2025,7 +2025,9 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t 
*ppos,
        struct sock *sk = sock->sk;
        struct tls_msg *tlm;
        struct sk_buff *skb;
+       bool released = true;
        ssize_t copied = 0;
+       int nodata = 0;
        bool nonblock;
        int chunk;
        int err;
@@ -2042,12 +2044,13 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t 
*ppos,
        if (err)
                goto splice_read_end;
 
+retry:
        if (!skb_queue_empty(&ctx->rx_list)) {
                skb = __skb_dequeue(&ctx->rx_list);
        } else {
                struct tls_decrypt_arg darg;
 
-               err = tls_rx_rec_wait(sk, nonblock, true, false);
+               err = tls_rx_rec_wait(sk, nonblock, released, false);
                if (err <= 0)
                        goto splice_read_end;
 
@@ -2059,6 +2062,9 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t 
*ppos,
 
                tls_rx_rec_done(ctx);
                skb = darg.skb;
+
+               /* The retry's wait runs with the socket lock still held. */
+               released = false;
        }
 
        rxm = strp_msg(skb);
@@ -2070,6 +2076,24 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t 
*ppos,
                goto splice_requeue;
        }
 
+       /* Splicing zero bytes reads as EOF to the caller. */
+       if (rxm->full_len == 0) {
+               consume_skb(skb);
+               if (++nodata >= TLS_RX_NODATA_LIMIT) {
+                       err = -EAGAIN;
+                       goto splice_read_end;
+               }
+               if (signal_pending(current)) {
+                       long timeo = sock_rcvtimeo(sk, nonblock);
+
+                       err = -EAGAIN;
+                       if (timeo)
+                               err = sock_intr_errno(timeo);
+                       goto splice_read_end;
+               }
+               goto retry;
+       }
+
        chunk = min_t(unsigned int, rxm->full_len, len);
        copied = skb_splice_bits(skb, sk, rxm->offset, pipe, chunk, flags);
        if (copied < 0)

-- 
2.55.0


Reply via email to