net/tls: Don't recursively call push_record during tls_write_space callbacks
authorDave Watson <davejwatson@fb.com>
Tue, 1 May 2018 20:05:39 +0000 (13:05 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 1 May 2018 22:57:52 +0000 (18:57 -0400)
It is reported that in some cases, write_space may be called in
do_tcp_sendpages, such that we recursively invoke do_tcp_sendpages again:

[  660.468802]  ? do_tcp_sendpages+0x8d/0x580
[  660.468826]  ? tls_push_sg+0x74/0x130 [tls]
[  660.468852]  ? tls_push_record+0x24a/0x390 [tls]
[  660.468880]  ? tls_write_space+0x6a/0x80 [tls]
...

tls_push_sg already does a loop over all sending sg's, so ignore
any tls_write_space notifications until we are done sending.
We then have to call the previous write_space to wake up
poll() waiters after we are done with the send loop.

Reported-by: Andre Tomt <andre@tomt.net>
Signed-off-by: Dave Watson <davejwatson@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tls.h
net/tls/tls_main.c

index 3da8e13a6d9661c399d6d88f4b1d78c41aee4bb3..b400d0bb744831b10eb6271721163c1b75c5b510 100644 (file)
@@ -148,6 +148,7 @@ struct tls_context {
        struct scatterlist *partially_sent_record;
        u16 partially_sent_offset;
        unsigned long flags;
+       bool in_tcp_sendpages;
 
        u16 pending_open_record_frags;
        int (*push_pending_record)(struct sock *sk, int flags);
index 0d379970960e6c2a101fbb489237f97f0edf68bc..cc03e00785c7ffefc8c37cac39aecc7e28cc86f9 100644 (file)
@@ -114,6 +114,7 @@ int tls_push_sg(struct sock *sk,
        size = sg->length - offset;
        offset += sg->offset;
 
+       ctx->in_tcp_sendpages = true;
        while (1) {
                if (sg_is_last(sg))
                        sendpage_flags = flags;
@@ -148,6 +149,8 @@ retry:
        }
 
        clear_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags);
+       ctx->in_tcp_sendpages = false;
+       ctx->sk_write_space(sk);
 
        return 0;
 }
@@ -217,6 +220,10 @@ static void tls_write_space(struct sock *sk)
 {
        struct tls_context *ctx = tls_get_ctx(sk);
 
+       /* We are already sending pages, ignore notification */
+       if (ctx->in_tcp_sendpages)
+               return;
+
        if (!sk->sk_write_pending && tls_is_pending_closed_record(ctx)) {
                gfp_t sk_allocation = sk->sk_allocation;
                int rc;