net: socket: implement 64-bit timestamps
authorArnd Bergmann <arnd@arndb.de>
Wed, 17 Apr 2019 20:56:11 +0000 (22:56 +0200)
committerDavid S. Miller <davem@davemloft.net>
Fri, 19 Apr 2019 21:07:40 +0000 (14:07 -0700)
The 'timeval' and 'timespec' data structures used for socket timestamps
are going to be redefined in user space based on 64-bit time_t in future
versions of the C library to deal with the y2038 overflow problem,
which breaks the ABI definition.

Unlike many modern ioctl commands, SIOCGSTAMP and SIOCGSTAMPNS do not
use the _IOR() macro to encode the size of the transferred data, so it
remains ambiguous whether the application uses the old or new layout.

The best workaround I could find is rather ugly: we redefine the command
code based on the size of the respective data structure with a ternary
operator. This lets it get evaluated as late as possible, hopefully after
that structure is visible to the caller. We cannot use an #ifdef here,
because inux/sockios.h might have been included before any libc header
that could determine the size of time_t.

The ioctl implementation now interprets the new command codes as always
referring to the 64-bit structure on all architectures, while the old
architecture specific command code still refers to the old architecture
specific layout. The new command number is only used when they are
actually different.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/alpha/include/uapi/asm/sockios.h
arch/mips/include/uapi/asm/sockios.h
arch/sh/include/uapi/asm/sockios.h
arch/xtensa/include/uapi/asm/sockios.h
include/uapi/asm-generic/sockios.h
include/uapi/linux/sockios.h
net/socket.c

index ba287e4b01bfb5e1bd137cf28c7e5410aa1271ec..af92bc27c3be49c84b433a97ffd3b991e61d61a1 100644 (file)
@@ -11,7 +11,7 @@
 #define SIOCSPGRP      _IOW('s', 8, pid_t)
 #define SIOCGPGRP      _IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
-#define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
+#define SIOCGSTAMP_OLD 0x8906          /* Get stamp (timeval) */
+#define SIOCGSTAMPNS_OLD 0x8907                /* Get stamp (timespec) */
 
 #endif /* _ASM_ALPHA_SOCKIOS_H */
index 5b40a88593fa8180e9af018d98535a6717bef614..66f60234f290b5a1ae3e0788dccf1e0283d80ce5 100644 (file)
@@ -21,7 +21,7 @@
 #define SIOCSPGRP      _IOW('s', 8, pid_t)
 #define SIOCGPGRP      _IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
-#define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
+#define SIOCGSTAMP_OLD 0x8906          /* Get stamp (timeval) */
+#define SIOCGSTAMPNS_OLD 0x8907                /* Get stamp (timespec) */
 
 #endif /* _ASM_SOCKIOS_H */
index 17313d2c35272ea740dd2b65b2dab0e91925213d..ef18a668456d74df9759e4e0cb54487e0685781c 100644 (file)
@@ -10,6 +10,7 @@
 #define SIOCSPGRP      _IOW('s', 8, pid_t)
 #define SIOCGPGRP      _IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP     _IOR('s', 100, struct timeval) /* Get stamp (timeval) */
-#define SIOCGSTAMPNS   _IOR('s', 101, struct timespec) /* Get stamp (timespec) */
+#define SIOCGSTAMP_OLD _IOR('s', 100, struct timeval) /* Get stamp (timeval) */
+#define SIOCGSTAMPNS_OLD _IOR('s', 101, struct timespec) /* Get stamp (timespec) */
+
 #endif /* __ASM_SH_SOCKIOS_H */
index fb8ac36071894fd47a09f0a2970f046ba480c458..1a1f58f4b75a293cf355a5638ad97a06b0de7626 100644 (file)
@@ -26,7 +26,7 @@
 #define SIOCSPGRP      _IOW('s', 8, pid_t)
 #define SIOCGPGRP      _IOR('s', 9, pid_t)
 
-#define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
-#define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
+#define SIOCGSTAMP_OLD 0x8906          /* Get stamp (timeval) */
+#define SIOCGSTAMPNS_OLD 0x8907                /* Get stamp (timespec) */
 
 #endif /* _XTENSA_SOCKIOS_H */
index 64f658c7cec2a90710394ca8b827bfe257bebc2e..44fa3ed7048322bf072e4ed21945f5ed9415e318 100644 (file)
@@ -8,7 +8,7 @@
 #define FIOGETOWN      0x8903
 #define SIOCGPGRP      0x8904
 #define SIOCATMARK     0x8905
-#define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
-#define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
+#define SIOCGSTAMP_OLD 0x8906          /* Get stamp (timeval) */
+#define SIOCGSTAMPNS_OLD 0x8907                /* Get stamp (timespec) */
 
 #endif /* __ASM_GENERIC_SOCKIOS_H */
index d393e9ed396426125152a0583a6837274e111e0f..7d1bccbbef781f2c0064b568ee22dba135b5b87e 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef _LINUX_SOCKIOS_H
 #define _LINUX_SOCKIOS_H
 
+#include <asm/bitsperlong.h>
 #include <asm/sockios.h>
 
 /* Linux-specific socket ioctls */
 
 #define SOCK_IOC_TYPE  0x89
 
+/*
+ * the timeval/timespec data structure layout is defined by libc,
+ * so we need to cover both possible versions on 32-bit.
+ */
+/* Get stamp (timeval) */
+#define SIOCGSTAMP_NEW  _IOR(SOCK_IOC_TYPE, 0x06, long long[2])
+/* Get stamp (timespec) */
+#define SIOCGSTAMPNS_NEW _IOR(SOCK_IOC_TYPE, 0x07, long long[2])
+
+#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+/* on 64-bit and x32, avoid the ?: operator */
+#define SIOCGSTAMP     SIOCGSTAMP_OLD
+#define SIOCGSTAMPNS   SIOCGSTAMPNS_OLD
+#else
+#define SIOCGSTAMP     ((sizeof(struct timeval))  == 8 ? \
+                        SIOCGSTAMP_OLD   : SIOCGSTAMP_NEW)
+#define SIOCGSTAMPNS   ((sizeof(struct timespec)) == 8 ? \
+                        SIOCGSTAMPNS_OLD : SIOCGSTAMPNS_NEW)
+#endif
+
 /* Routing table calls. */
 #define SIOCADDRT      0x890B          /* add routing table entry      */
 #define SIOCDELRT      0x890C          /* delete routing table entry   */
index ab624d42ead50e0f2b20c8d2b6838b28cf8d0021..8d9d4fc7d962ace489486412fb4a40ac5fef16de 100644 (file)
@@ -1164,14 +1164,24 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 
                        err = open_related_ns(&net->ns, get_net_ns);
                        break;
-               case SIOCGSTAMP:
-               case SIOCGSTAMPNS:
+               case SIOCGSTAMP_OLD:
+               case SIOCGSTAMPNS_OLD:
                        if (!sock->ops->gettstamp) {
                                err = -ENOIOCTLCMD;
                                break;
                        }
                        err = sock->ops->gettstamp(sock, argp,
-                                                  cmd == SIOCGSTAMP, false);
+                                                  cmd == SIOCGSTAMP_OLD,
+                                                  !IS_ENABLED(CONFIG_64BIT));
+               case SIOCGSTAMP_NEW:
+               case SIOCGSTAMPNS_NEW:
+                       if (!sock->ops->gettstamp) {
+                               err = -ENOIOCTLCMD;
+                               break;
+                       }
+                       err = sock->ops->gettstamp(sock, argp,
+                                                  cmd == SIOCGSTAMP_NEW,
+                                                  false);
                        break;
                default:
                        err = sock_do_ioctl(net, sock, cmd, arg);
@@ -3324,11 +3334,11 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
        case SIOCADDRT:
        case SIOCDELRT:
                return routing_ioctl(net, sock, cmd, argp);
-       case SIOCGSTAMP:
-       case SIOCGSTAMPNS:
+       case SIOCGSTAMP_OLD:
+       case SIOCGSTAMPNS_OLD:
                if (!sock->ops->gettstamp)
                        return -ENOIOCTLCMD;
-               return sock->ops->gettstamp(sock, argp, cmd == SIOCGSTAMP,
+               return sock->ops->gettstamp(sock, argp, cmd == SIOCGSTAMP_OLD,
                                            !COMPAT_USE_64BIT_TIME);
 
        case SIOCBONDSLAVEINFOQUERY:
@@ -3348,6 +3358,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
        case SIOCADDDLCI:
        case SIOCDELDLCI:
        case SIOCGSKNS:
+       case SIOCGSTAMP_NEW:
+       case SIOCGSTAMPNS_NEW:
                return sock_ioctl(file, cmd, arg);
 
        case SIOCGIFFLAGS: