tcp: avoid integer overflows in tcp_rcv_space_adjust()
authorEric Dumazet <edumazet@google.com>
Mon, 11 Dec 2017 01:55:03 +0000 (17:55 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 12 Dec 2017 15:53:04 +0000 (10:53 -0500)
When using large tcp_rmem[2] values (I did tests with 500 MB),
I noticed overflows while computing rcvwin.

Lets fix this before the following patch.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
Acked-by: Wei Wang <weiwan@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/tcp.h
net/ipv4/tcp_input.c

index ca4a636..4f93f09 100644 (file)
@@ -344,7 +344,7 @@ struct tcp_sock {
 
 /* Receiver queue space */
        struct {
-               int     space;
+               u32     space;
                u32     seq;
                u64     time;
        } rcvq_space;
index 746a677..2900e58 100644 (file)
@@ -576,8 +576,8 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk,
 void tcp_rcv_space_adjust(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
+       u32 copied;
        int time;
-       int copied;
 
        tcp_mstamp_refresh(tp);
        time = tcp_stamp_us_delta(tp->tcp_mstamp, tp->rcvq_space.time);
@@ -600,12 +600,13 @@ void tcp_rcv_space_adjust(struct sock *sk)
 
        if (sock_net(sk)->ipv4.sysctl_tcp_moderate_rcvbuf &&
            !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
-               int rcvwin, rcvmem, rcvbuf;
+               int rcvmem, rcvbuf;
+               u64 rcvwin;
 
                /* minimal window to cope with packet losses, assuming
                 * steady state. Add some cushion because of small variations.
                 */
-               rcvwin = (copied << 1) + 16 * tp->advmss;
+               rcvwin = ((u64)copied << 1) + 16 * tp->advmss;
 
                /* If rate increased by 25%,
                 *      assume slow start, rcvwin = 3 * copied
@@ -625,8 +626,9 @@ void tcp_rcv_space_adjust(struct sock *sk)
                while (tcp_win_from_space(sk, rcvmem) < tp->advmss)
                        rcvmem += 128;
 
-               rcvbuf = min(rcvwin / tp->advmss * rcvmem,
-                            sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
+               do_div(rcvwin, tp->advmss);
+               rcvbuf = min_t(u64, rcvwin * rcvmem,
+                              sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
                if (rcvbuf > sk->sk_rcvbuf) {
                        sk->sk_rcvbuf = rcvbuf;