• Lance Richardson's avatar
    net: support compat 64-bit time in {s,g}etsockopt · 51d2a5e7
    Lance Richardson authored
    [ Upstream commit 988bf7243e03ef69238381594e0334a79cef74a6 ]
    
    For the x32 ABI, struct timeval has two 64-bit fields. However
    the kernel currently interprets the user-space values used for
    the SO_RCVTIMEO and SO_SNDTIMEO socket options as having a pair
    of 32-bit fields.
    
    When the seconds portion of the requested timeout is less than 2**32,
    the seconds portion of the effective timeout is correct but the
    microseconds portion is zero.  When the seconds portion of the
    requested timeout is zero and the microseconds portion is non-zero,
    the kernel interprets the timeout as zero (never timeout).
    
    Fix by using 64-bit time for SO_RCVTIMEO/SO_SNDTIMEO as required
    for the ABI.
    
    The code included below demonstrates the problem.
    
    Results before patch:
        $ gcc -m64 -Wall -O2 -o socktmo socktmo.c && ./socktmo
        recv time: 2.008181 seconds
        send time: 2.015985 seconds
    
        $ gcc -m32 -Wall -O2 -o socktmo socktmo.c && ./socktmo
        recv time: 2.016763 seconds
        send time: 2.016062 seconds
    
        $ gcc -mx32 -Wall -O2 -o socktmo socktmo.c && ./socktmo
        recv time: 1.007239 seconds
        send time: 1.023890 seconds
    
    Results after patch:
        $ gcc -m64 -O2 -Wall -o socktmo socktmo.c && ./socktmo
        recv time: 2.010062 seconds
        send time: 2.015836 seconds
    
        $ gcc -m32 -O2 -Wall -o socktmo socktmo.c && ./socktmo
        recv time: 2.013974 seconds
        send time: 2.015981 seconds
    
        $ gcc -mx32 -O2 -Wall -o socktmo socktmo.c && ./socktmo
        recv time: 2.030257 seconds
        send time: 2.013383 seconds
    
     #include <stdio.h>
     #include <stdlib.h>
     #include <sys/socket.h>
     #include <sys/types.h>
     #include <sys/time.h>
    
     void checkrc(char *str, int rc)
     {
             if (rc >= 0)
                     return;
    
             perror(str);
             exit(1);
     }
    
     static char buf[1024];
     int main(int argc, char **argv)
     {
             int rc;
             int socks[2];
             struct timeval tv;
             struct timeval start, end, delta;
    
             rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
             checkrc("socketpair", rc);
    
             /* set timeout to 1.999999 seconds */
             tv.tv_sec = 1;
             tv.tv_usec = 999999;
             rc = setsockopt(socks[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
             rc = setsockopt(socks[0], SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
             checkrc("setsockopt", rc);
    
             /* measure actual receive timeout */
             gettimeofday(&start, NULL);
             rc = recv(socks[0], buf, sizeof buf, 0);
             gettimeofday(&end, NULL);
             timersub(&end, &start, &delta);
    
             printf("recv time: %ld.%06ld seconds\n",
                    (long)delta.tv_sec, (long)delta.tv_usec);
    
             /* fill send buffer */
             do {
                     rc = send(socks[0], buf, sizeof buf, 0);
             } while (rc > 0);
    
             /* measure actual send timeout */
             gettimeofday(&start, NULL);
             rc = send(socks[0], buf, sizeof buf, 0);
             gettimeofday(&end, NULL);
             timersub(&end, &start, &delta);
    
             printf("send time: %ld.%06ld seconds\n",
                    (long)delta.tv_sec, (long)delta.tv_usec);
             exit(0);
     }
    
    Fixes: 515c7af8 ("x32: Use compat shims for {g,s}etsockopt")
    Reported-by: 's avatarGopal RajagopalSai <gopalsr83@gmail.com>
    Signed-off-by: 's avatarLance Richardson <lance.richardson.net@gmail.com>
    Signed-off-by: 's avatarDavid S. Miller <davem@davemloft.net>
    Signed-off-by: 's avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    51d2a5e7