[PATCH net-next v2 1/6] net/tls: Bound consecutive no-data records in tls_sw_read_sock()
Chuck Lever <[email protected]> Mon, 20 Jul 2026 10:27:55 -0400
| Newsgroups | dev.linux.lists.kernel-tls-handshake,org.kernel.vger.linux-kselftest,org.kernel.vger.linux-nfs,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
A record that delivers no payload -- an empty TLS 1.3 data record today, a control record once read_sock_rectype() lands -- leaves tls_sw_read_sock() in its loop without advancing the caller's read descriptor. A peer that streams such records keeps the receive loop running, and the socket lock held, for as long as the records arrive. Cap the number of consecutive no-data records consumed per call. The count resets on any record that delivers bytes, so a normal stream is unaffected; a peer supplying only empty records is bounded to TLS_RX_NODATA_LIMIT iterations before the call returns 0. read_sock consumers treat that as "no progress, re-poll" rather than EOF, so the connection stays up and makes progress once real data arrives. Only tls_sw_read_sock() needs this cap. Its consumers drive the receive loop from kernel context -- a work item or service thread holding the socket lock across the whole call with no return to userspace -- so an unbounded empty-record stream keeps that context and the lock pinned for as long as the flood lasts. The cap supplies the return boundary that a system call would otherwise provide. tls_sw_splice_read() and tls_sw_recvmsg() already have one: they run in the calling task's context, reschedule while draining the socket backlog (cond_resched() in __release_sock()), and drop the socket lock when the call returns. A flood there costs the caller only its own scheduler time, so the cap would add nothing. Signed-off-by: Chuck Lever <[email protected]> --- net/tls/tls_sw.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index d4afc90fd796..087950ca639c 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2049,6 +2049,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, goto splice_read_end; } +/* Consecutive empty data records deliver no bytes; cap them per + * call so a peer streaming them cannot hold the socket lock here. + */ +#define TLS_RX_NODATA_LIMIT 16 + int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, sk_read_actor_t read_actor) { @@ -2057,6 +2062,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, struct tls_prot_info *prot = &tls_ctx->prot_info; struct strp_msg *rxm = NULL; struct sk_buff *skb = NULL; + unsigned int nodata_count = 0; struct sk_psock *psock; size_t flushed_at = 0; bool released = true; @@ -2122,7 +2128,13 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, * here instead. */ if (rxm->full_len == 0) { + err = 0; consume_skb(skb); + /* tls_rx_reader_release() announces any parsed record + * on exit, so returning 0 here cannot strand it. + */ + if (++nodata_count >= TLS_RX_NODATA_LIMIT) + break; continue; } @@ -2133,6 +2145,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_requeue; } copied += used; + nodata_count = 0; if (used < rxm->full_len) { rxm->offset += used; rxm->full_len -= used; -- 2.54.0