From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1791) id CC036398B438; Fri, 26 Feb 2021 20:41:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CC036398B438 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Adhemerval Zanella To: glibc-cvs@sourceware.org Subject: [glibc/azanella/y2038] linux: Add fallback for 64-bit time_t SO_TIMESTAMP{NS} X-Act-Checkin: glibc X-Git-Author: Adhemerval Zanella X-Git-Refname: refs/heads/azanella/y2038 X-Git-Oldrev: 81e1ecfaca0c228c85cdf9319975153a3a846460 X-Git-Newrev: 256b4aa78253c9664ba198f11961b86117490bad Message-Id: <20210226204103.CC036398B438@sourceware.org> Date: Fri, 26 Feb 2021 20:41:03 +0000 (GMT) X-BeenThere: glibc-cvs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Glibc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 26 Feb 2021 20:41:03 -0000 https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=256b4aa78253c9664ba198f11961b86117490bad commit 256b4aa78253c9664ba198f11961b86117490bad Author: Adhemerval Zanella Date: Mon Sep 7 17:08:46 2020 -0300 linux: Add fallback for 64-bit time_t SO_TIMESTAMP{NS} The constant value will be changed for __TIMESIZE=64, so binaries built with 64-bit time support might fail to work properly on old kernels. Both {get,set}sockopt will retry the syscall with the old constant values and the timeout value adjusted when kernel returns ENOTPROTOPT. The recvmsg handling is more complicated because it requires check the returned kernel control message and make some convertions. For !__ASSUME_TIME64_SYSCALLS it converts the first 32-bit time SO_TIMESTAMP or SO_TIMESTAMPNS and appends it to the control buffer if has extra space or returns MSG_CTRUNC otherwise. The 32-bit time field is kept as-is. Calls with __TIMESIZE=32 will see the converted 64-bit time control messages as spurious control message of unknown type. Calls with __TIMESIZE=64 running on pre-time64 kernels will see the original message as a spurious control ones of unknown typ while running on kernel with native 64-bit time support will only see the time64 version of the control message. Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15 kernel). Diff: --- include/sys/socket.h | 5 + sysdeps/unix/sysv/linux/Makefile | 2 +- sysdeps/unix/sysv/linux/Versions | 1 + sysdeps/unix/sysv/linux/bits/socket-constants.h | 7 ++ sysdeps/unix/sysv/linux/convert_scm_timestamps.c | 102 +++++++++++++++++++++ sysdeps/unix/sysv/linux/getsockopt.c | 12 +++ .../unix/sysv/linux/hppa/bits/socket-constants.h | 7 ++ .../unix/sysv/linux/mips/bits/socket-constants.h | 7 ++ .../sysv/linux/powerpc/bits/socket-constants.h | 7 ++ sysdeps/unix/sysv/linux/recvmsg.c | 23 +++-- sysdeps/unix/sysv/linux/setsockopt.c | 16 +++- .../unix/sysv/linux/sparc/bits/socket-constants.h | 7 ++ 12 files changed, 188 insertions(+), 8 deletions(-) diff --git a/include/sys/socket.h b/include/sys/socket.h index 0e39dd2a3a..15d4a62b26 100644 --- a/include/sys/socket.h +++ b/include/sys/socket.h @@ -164,5 +164,10 @@ libc_hidden_proto (__libc_sa_len) libc_hidden_proto (__cmsg_nxthdr) +#ifndef __ASSUME_TIME64_SYSCALLS +extern void __convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) ; +libc_hidden_proto (__convert_scm_timestamps) +#endif + #endif #endif diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 488114a41e..1f99f00a82 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -64,7 +64,7 @@ sysdep_routines += adjtimex clone umount umount2 readahead sysctl \ time64-support pselect32 \ xstat fxstat lxstat xstat64 fxstat64 lxstat64 \ fxstatat fxstatat64 \ - xmknod xmknodat + xmknod xmknodat convert_scm_timestamps CFLAGS-gethostid.c = -fexceptions CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions index c35f783e2a..257ccbd3b4 100644 --- a/sysdeps/unix/sysv/linux/Versions +++ b/sysdeps/unix/sysv/linux/Versions @@ -177,6 +177,7 @@ libc { __pread64_nocancel; __close_nocancel; __sigtimedwait; + __convert_scm_timestamps; # functions used by nscd __netlink_assert_response; } diff --git a/sysdeps/unix/sysv/linux/bits/socket-constants.h b/sysdeps/unix/sysv/linux/bits/socket-constants.h index d4e9979605..d1054eda1f 100644 --- a/sysdeps/unix/sysv/linux/bits/socket-constants.h +++ b/sysdeps/unix/sysv/linux/bits/socket-constants.h @@ -53,3 +53,10 @@ #define SO_SNDTIMEO_OLD 21 #define SO_RCVTIMEO_NEW 66 #define SO_SNDTIMEO_NEW 67 + +#define SO_TIMESTAMP_OLD 29 +#define SO_TIMESTAMPNS_OLD 35 +#define SO_TIMESTAMPING_OLD 37 +#define SO_TIMESTAMP_NEW 63 +#define SO_TIMESTAMPNS_NEW 64 +#define SO_TIMESTAMPING_NEW 65 diff --git a/sysdeps/unix/sysv/linux/convert_scm_timestamps.c b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c new file mode 100644 index 0000000000..3c7fda26ee --- /dev/null +++ b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c @@ -0,0 +1,102 @@ +/* Socket timestamp conversion routines. + Copyright (C) 2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#ifndef __ASSUME_TIME64_SYSCALLS +# include +# include +# include +/* The kernel header with SO_* constants is used as default for _GNU_SOURCE, + however the new constants that describe 64-bit time support were added + only on v5.1. */ +# if !defined(SO_TIMESTAMP_OLD) || !defined(SO_TIMESTAMP_NEW) \ + || !defined(SO_TIMESTAMPNS_OLD) || !defined(SO_TIMESTAMPNS_NEW) +# include +# endif + +/* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and + appends it to the control buffer. The 32-bit time field is kept as-is. + + Calls with __TIMESIZE=32 will see the converted 64-bit time control + messages as spurious control message of unknown type. + + Calls with __TIMESIZE=64 running on pre-time64 kernels will see the + original message as a spurious control ones of unknown typ while running + on kernel with native 64-bit time support will only see the time64 version + of the control message. */ +void +__convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) +{ + if (msg->msg_control == NULL || msg->msg_controllen == 0) + return; + + /* The returnted control message format for SO_TIMESTAMP_NEW is a + 'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a + 'struct __kernel_timespec'. In both case it is essentially two + uint64_t members. */ + uint64_t tvts[2]; + + struct cmsghdr *cmsg, *last = NULL; + int type = 0; + + for (cmsg = CMSG_FIRSTHDR (msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR (msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + switch (cmsg->cmsg_type) + { + case SO_TIMESTAMP_OLD: + if (type != 0) + break; + type = SO_TIMESTAMP_NEW; + goto common; + + case SO_TIMESTAMPNS_OLD: + type = SO_TIMESTAMPNS_NEW; + + /* fallthrough */ + common: + memcpy (tvts, CMSG_DATA (cmsg), sizeof (tvts)); + break; + } + + last = cmsg; + } + + if (last == NULL || type == 0) + return; + + if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen) + { + msg->msg_flags |= MSG_CTRUNC; + return; + } + + msg->msg_controllen += CMSG_SPACE (sizeof tvts); + cmsg = CMSG_NXTHDR(msg, last); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = type; + cmsg->cmsg_len = CMSG_LEN (sizeof tvts); + memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts); +} +libc_hidden_def (__convert_scm_timestamps) +#endif diff --git a/sysdeps/unix/sysv/linux/getsockopt.c b/sysdeps/unix/sysv/linux/getsockopt.c index a093e42f96..0cdf6cd011 100644 --- a/sysdeps/unix/sysv/linux/getsockopt.c +++ b/sysdeps/unix/sysv/linux/getsockopt.c @@ -72,6 +72,18 @@ getsockopt32 (int fd, int level, int optname, void *optval, *tv64 = valid_timeval32_to_timeval64 (tv32); *len = sizeof (*tv64); } + break; + + case SO_TIMESTAMP_NEW: + case SO_TIMESTAMPNS_NEW: + { + if (optname == SO_TIMESTAMP_NEW) + optname = SO_TIMESTAMP_OLD; + if (optname == SO_TIMESTAMPNS_NEW) + optname = SO_TIMESTAMPNS_OLD; + r = getsockopt_syscall (fd, level, optname, optval, len); + } + break; } return r; diff --git a/sysdeps/unix/sysv/linux/hppa/bits/socket-constants.h b/sysdeps/unix/sysv/linux/hppa/bits/socket-constants.h index 2413529e71..aa09ab5958 100644 --- a/sysdeps/unix/sysv/linux/hppa/bits/socket-constants.h +++ b/sysdeps/unix/sysv/linux/hppa/bits/socket-constants.h @@ -41,3 +41,10 @@ #define SO_SNDTIMEO_OLD 4101 #define SO_RCVTIMEO_NEW 16448 #define SO_SNDTIMEO_NEW 16449 + +#define SO_TIMESTAMP_OLD 0x4012 +#define SO_TIMESTAMPNS_OLD 0x4013 +#define SO_TIMESTAMPING_OLD 0x4020 +#define SO_TIMESTAMP_NEW 0x4038 +#define SO_TIMESTAMPNS_NEW 0x4039 +#define SO_TIMESTAMPING_NEW 0x403A diff --git a/sysdeps/unix/sysv/linux/mips/bits/socket-constants.h b/sysdeps/unix/sysv/linux/mips/bits/socket-constants.h index 2c124234fc..4a5d235aed 100644 --- a/sysdeps/unix/sysv/linux/mips/bits/socket-constants.h +++ b/sysdeps/unix/sysv/linux/mips/bits/socket-constants.h @@ -41,3 +41,10 @@ #define SO_SNDTIMEO_OLD 4101 #define SO_RCVTIMEO_NEW 66 #define SO_SNDTIMEO_NEW 67 + +#define SO_TIMESTAMP_OLD 29 +#define SO_TIMESTAMPNS_OLD 35 +#define SO_TIMESTAMPING_OLD 37 +#define SO_TIMESTAMP_NEW 63 +#define SO_TIMESTAMPNS_NEW 64 +#define SO_TIMESTAMPING_NEW 65 diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/socket-constants.h b/sysdeps/unix/sysv/linux/powerpc/bits/socket-constants.h index 682a3f4608..ff42df6f5f 100644 --- a/sysdeps/unix/sysv/linux/powerpc/bits/socket-constants.h +++ b/sysdeps/unix/sysv/linux/powerpc/bits/socket-constants.h @@ -41,3 +41,10 @@ #define SO_SNDTIMEO_OLD 19 #define SO_RCVTIMEO_NEW 66 #define SO_SNDTIMEO_NEW 67 + +#define SO_TIMESTAMP_OLD 29 +#define SO_TIMESTAMPNS_OLD 35 +#define SO_TIMESTAMPING_OLD 37 +#define SO_TIMESTAMP_NEW 63 +#define SO_TIMESTAMPNS_NEW 64 +#define SO_TIMESTAMPING_NEW 65 diff --git a/sysdeps/unix/sysv/linux/recvmsg.c b/sysdeps/unix/sysv/linux/recvmsg.c index b209b4ad99..a2a600228b 100644 --- a/sysdeps/unix/sysv/linux/recvmsg.c +++ b/sysdeps/unix/sysv/linux/recvmsg.c @@ -19,16 +19,27 @@ #include #include #include -#include ssize_t __libc_recvmsg (int fd, struct msghdr *msg, int flags) { -# ifdef __ASSUME_RECVMSG_SYSCALL - return SYSCALL_CANCEL (recvmsg, fd, msg, flags); -# else - return SOCKETCALL_CANCEL (recvmsg, fd, msg, flags); -# endif + ssize_t r; +#ifndef __ASSUME_TIME64_SYSCALLS + socklen_t orig_controllen = msg->msg_controllen; +#endif + +#ifdef __ASSUME_RECVMSG_SYSCALL + r = SYSCALL_CANCEL (recvmsg, fd, msg, flags); +#else + r = SOCKETCALL_CANCEL (recvmsg, fd, msg, flags); +#endif + +#ifndef __ASSUME_TIME64_SYSCALLS + if (r >= 0) + __convert_scm_timestamps (msg, orig_controllen); +#endif + + return r; } weak_alias (__libc_recvmsg, recvmsg) weak_alias (__libc_recvmsg, __recvmsg) diff --git a/sysdeps/unix/sysv/linux/setsockopt.c b/sysdeps/unix/sysv/linux/setsockopt.c index a1f98a937d..59a53d2d2d 100644 --- a/sysdeps/unix/sysv/linux/setsockopt.c +++ b/sysdeps/unix/sysv/linux/setsockopt.c @@ -19,7 +19,9 @@ /* The kernel header with SO_* constants is used as default for _GNU_SOURCE, however the new constants that describe 64-bit time support were added only on v5.1. */ -#if !defined(SO_RCVTIMEO_NEW) || !defined(SO_RCVTIMEO_OLD) +#if !defined(SO_RCVTIMEO_NEW) || !defined(SO_RCVTIMEO_OLD) \ + || !defined(SO_TIMESTAMP_OLD) || !defined(SO_TIMESTAMP_NEW) \ + || !defined(SO_TIMESTAMPNS_OLD) || !defined(SO_TIMESTAMPNS_NEW) # include #endif #include @@ -74,6 +76,18 @@ setsockopt32 (int fd, int level, int optname, const void *optval, r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32)); } + break; + + case SO_TIMESTAMP_NEW: + case SO_TIMESTAMPNS_NEW: + { + if (optname == SO_TIMESTAMP_NEW) + optname = SO_TIMESTAMP_OLD; + if (optname == SO_TIMESTAMPNS_NEW) + optname = SO_TIMESTAMPNS_OLD; + r = setsockopt_syscall (fd, level, optname, NULL, 0); + } + break; } return r; diff --git a/sysdeps/unix/sysv/linux/sparc/bits/socket-constants.h b/sysdeps/unix/sysv/linux/sparc/bits/socket-constants.h index 3eae2176dd..bfd7eef233 100644 --- a/sysdeps/unix/sysv/linux/sparc/bits/socket-constants.h +++ b/sysdeps/unix/sysv/linux/sparc/bits/socket-constants.h @@ -41,3 +41,10 @@ #define SO_SNDTIMEO_OLD 16384 #define SO_RCVTIMEO_NEW 68 #define SO_SNDTIMEO_NEW 69 + +#define SO_TIMESTAMP_OLD 0x001d +#define SO_TIMESTAMPNS_OLD 0x0021 +#define SO_TIMESTAMPING_OLD 0x0023 +#define SO_TIMESTAMP_NEW 0x0046 +#define SO_TIMESTAMPNS_NEW 0x0042 +#define SO_TIMESTAMPING_NEW 0x0043