svcrpc: fix UDP on servers with lots of threads
authorJ. Bruce Fields <bfields@redhat.com>
Wed, 20 Feb 2019 17:54:50 +0000 (12:54 -0500)
committerJ. Bruce Fields <bfields@redhat.com>
Thu, 21 Feb 2019 15:17:36 +0000 (10:17 -0500)
James Pearson found that an NFS server stopped responding to UDP
requests if started with more than 1017 threads.

sv_max_mesg is about 2^20, so that is probably where the calculation
performed by

svc_sock_setbufsize(svsk->sk_sock,
                            (serv->sv_nrthreads+3) * serv->sv_max_mesg,
                            (serv->sv_nrthreads+3) * serv->sv_max_mesg);

starts to overflow an int.

Reported-by: James Pearson <jcpearson@gmail.com>
Tested-by: James Pearson <jcpearson@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
net/sunrpc/svcsock.c

index a6a0609..43590a9 100644 (file)
@@ -349,12 +349,16 @@ static ssize_t svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov,
 /*
  * Set socket snd and rcv buffer lengths
  */
-static void svc_sock_setbufsize(struct socket *sock, unsigned int snd,
-                               unsigned int rcv)
+static void svc_sock_setbufsize(struct svc_sock *svsk, unsigned int nreqs)
 {
+       unsigned int max_mesg = svsk->sk_xprt.xpt_server->sv_max_mesg;
+       struct socket *sock = svsk->sk_sock;
+
+       nreqs = min(nreqs, INT_MAX / 2 / max_mesg);
+
        lock_sock(sock->sk);
-       sock->sk->sk_sndbuf = snd * 2;
-       sock->sk->sk_rcvbuf = rcv * 2;
+       sock->sk->sk_sndbuf = nreqs * max_mesg * 2;
+       sock->sk->sk_rcvbuf = nreqs * max_mesg * 2;
        sock->sk->sk_write_space(sock->sk);
        release_sock(sock->sk);
 }
@@ -516,9 +520,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
             * provides an upper bound on the number of threads
             * which will access the socket.
             */
-           svc_sock_setbufsize(svsk->sk_sock,
-                               (serv->sv_nrthreads+3) * serv->sv_max_mesg,
-                               (serv->sv_nrthreads+3) * serv->sv_max_mesg);
+           svc_sock_setbufsize(svsk, serv->sv_nrthreads + 3);
 
        clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
        skb = NULL;
@@ -681,9 +683,7 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv)
         * receive and respond to one request.
         * svc_udp_recvfrom will re-adjust if necessary
         */
-       svc_sock_setbufsize(svsk->sk_sock,
-                           3 * svsk->sk_xprt.xpt_server->sv_max_mesg,
-                           3 * svsk->sk_xprt.xpt_server->sv_max_mesg);
+       svc_sock_setbufsize(svsk, 3);
 
        /* data might have come in before data_ready set up */
        set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);