[v2,05/25] linux: Add fallback for 64-bit time_t SO_{RCV, SND}TIMEO
Checks
Commit Message
The constant values 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.
It also adds an internal only SO_{RCV,SND}TIMEO where
COMPAT_SO_{RCV,SND}TIMEO_OLD indicates pre 32 bit time support and
COMPAT_SO_{RCV,SND}TIMEO_NEW indicates time64 support. It allows to
refer to constant independently of the time_t ABI and kernel version
used.
Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
kernel).
---
sysdeps/unix/sysv/linux/getsockopt.c | 67 +++++++++++++++--
.../sysv/linux/hppa/socket-constants-time64.h | 30 ++++++++
.../sysv/linux/mips/socket-constants-time64.h | 30 ++++++++
.../linux/powerpc/socket-constants-time64.h | 30 ++++++++
sysdeps/unix/sysv/linux/setsockopt.c | 71 ++++++++++++++++---
.../unix/sysv/linux/socket-constants-time64.h | 30 ++++++++
.../linux/sparc/socket-constants-time64.h | 30 ++++++++
7 files changed, 273 insertions(+), 15 deletions(-)
create mode 100644 sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
create mode 100644 sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
create mode 100644 sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
create mode 100644 sysdeps/unix/sysv/linux/socket-constants-time64.h
create mode 100644 sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
Comments
On Tue, 18 May 2021 17:55:53 -0300
Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote:
> The constant values 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.
>
> It also adds an internal only SO_{RCV,SND}TIMEO where
> COMPAT_SO_{RCV,SND}TIMEO_OLD indicates pre 32 bit time support and
> COMPAT_SO_{RCV,SND}TIMEO_NEW indicates time64 support. It allows to
> refer to constant independently of the time_t ABI and kernel version
> used.
>
> Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
> kernel).
Reviewed-by: Lukasz Majewski <lukma@denx.de>
> ---
> sysdeps/unix/sysv/linux/getsockopt.c | 67 +++++++++++++++--
> .../sysv/linux/hppa/socket-constants-time64.h | 30 ++++++++
> .../sysv/linux/mips/socket-constants-time64.h | 30 ++++++++
> .../linux/powerpc/socket-constants-time64.h | 30 ++++++++
> sysdeps/unix/sysv/linux/setsockopt.c | 71
> ++++++++++++++++--- .../unix/sysv/linux/socket-constants-time64.h |
> 30 ++++++++ .../linux/sparc/socket-constants-time64.h | 30
> ++++++++ 7 files changed, 273 insertions(+), 15 deletions(-)
> create mode 100644
> sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h create mode
> 100644 sysdeps/unix/sysv/linux/mips/socket-constants-time64.h create
> mode 100644 sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> create mode 100644 sysdeps/unix/sysv/linux/socket-constants-time64.h
> create mode 100644
> sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
>
> diff --git a/sysdeps/unix/sysv/linux/getsockopt.c
> b/sysdeps/unix/sysv/linux/getsockopt.c index 76ee8a94d6..c8e502d976
> 100644 --- a/sysdeps/unix/sysv/linux/getsockopt.c
> +++ b/sysdeps/unix/sysv/linux/getsockopt.c
> @@ -15,16 +15,15 @@
> License along with the GNU C Library; if not, see
> <https://www.gnu.org/licenses/>. */
>
> -#include <errno.h>
> -#include <signal.h>
> #include <sys/socket.h>
> -
> +#include <time.h>
> +#include <sysdep.h>
> #include <socketcall.h>
> -#include <kernel-features.h>
> -#include <sys/syscall.h>
> +#include <socket-constants-time64.h>
>
> -int
> -__getsockopt (int fd, int level, int optname, void *optval,
> socklen_t *len) +static int
> +getsockopt_syscall (int fd, int level, int optname, void *optval,
> + socklen_t *len)
> {
> #ifdef __ASSUME_GETSOCKOPT_SYSCALL
> return INLINE_SYSCALL (getsockopt, 5, fd, level, optname, optval,
> len); @@ -32,4 +31,58 @@ __getsockopt (int fd, int level, int
> optname, void *optval, socklen_t *len) return SOCKETCALL (getsockopt,
> fd, level, optname, optval, len); #endif
> }
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +static int
> +getsockopt32 (int fd, int level, int optname, void *optval,
> + socklen_t *len)
> +{
> + int r = -1;
> +
> + if (level != SOL_SOCKET)
> + return r;
> +
> + switch (optname)
> + {
> + case COMPAT_SO_RCVTIMEO_NEW:
> + case COMPAT_SO_SNDTIMEO_NEW:
> + {
> + if (*len < sizeof (struct __timeval64))
> + {
> + __set_errno (EINVAL);
> + break;
> + }
> +
> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
> + optname = COMPAT_SO_RCVTIMEO_OLD;
> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
> + optname = COMPAT_SO_SNDTIMEO_OLD;
> +
> + struct __timeval32 tv32;
> + r = getsockopt_syscall (fd, level, optname, &tv32,
> + (socklen_t[]) { sizeof tv32 });
> + if (r < 0)
> + break;
> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
> + *tv64 = valid_timeval32_to_timeval64 (tv32);
> + *len = sizeof (*tv64);
> + }
> + }
> +
> + return r;
> +}
> +#endif
> +
> +int
> +__getsockopt (int fd, int level, int optname, void *optval,
> socklen_t *len) +{
> + int r = getsockopt_syscall (fd, level, optname, optval, len);
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> + if (r == -1 && errno == ENOPROTOOPT)
> + r = getsockopt32 (fd, level, optname, optval, len);
> +#endif
> +
> + return r;
> +}
> weak_alias (__getsockopt, getsockopt)
> diff --git a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h new file
> mode 100644 index 0000000000..ea721e0fc2
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and
> 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 4102
> +#define COMPAT_SO_SNDTIMEO_OLD 4101
> +#define COMPAT_SO_RCVTIMEO_NEW 16448
> +#define COMPAT_SO_SNDTIMEO_NEW 16449
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h new file
> mode 100644 index 0000000000..ab8bd62853
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and
> 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 4102
> +#define COMPAT_SO_SNDTIMEO_OLD 4101
> +#define COMPAT_SO_RCVTIMEO_NEW 66
> +#define COMPAT_SO_SNDTIMEO_NEW 67
> +
> +#endif
> diff --git
> a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h new file
> mode 100644 index 0000000000..1e48dcca8d --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and
> 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 18
> +#define COMPAT_SO_SNDTIMEO_OLD 19
> +#define COMPAT_SO_RCVTIMEO_NEW 66
> +#define COMPAT_SO_SNDTIMEO_NEW 67
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/setsockopt.c
> b/sysdeps/unix/sysv/linux/setsockopt.c index 12fd7bdcde..6505202265
> 100644 --- a/sysdeps/unix/sysv/linux/setsockopt.c
> +++ b/sysdeps/unix/sysv/linux/setsockopt.c
> @@ -15,21 +15,76 @@
> License along with the GNU C Library; if not, see
> <https://www.gnu.org/licenses/>. */
>
> -#include <errno.h>
> -#include <signal.h>
> #include <sys/socket.h>
> -
> +#include <time.h>
> +#include <sysdep.h>
> #include <socketcall.h>
> -#include <kernel-features.h>
> -#include <sys/syscall.h>
> +#include <socket-constants-time64.h>
>
> -int
> -setsockopt (int fd, int level, int optname, const void *optval,
> socklen_t len) +static int
> +setsockopt_syscall (int fd, int level, int optname, const void
> *optval,
> + socklen_t len)
> {
> #ifdef __ASSUME_SETSOCKOPT_SYSCALL
> - return INLINE_SYSCALL (setsockopt, 5, fd, level, optname, optval,
> len);
> + return INLINE_SYSCALL_CALL (setsockopt, fd, level, optname,
> optval, len); #else
> return SOCKETCALL (setsockopt, fd, level, optname, optval, len);
> #endif
> }
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +static int
> +setsockopt32 (int fd, int level, int optname, const void *optval,
> + socklen_t len)
> +{
> + int r = -1;
> +
> + if (level != SOL_SOCKET)
> + return r;
> +
> + switch (optname)
> + {
> + case COMPAT_SO_RCVTIMEO_NEW:
> + case COMPAT_SO_SNDTIMEO_NEW:
> + {
> + if (len < sizeof (struct __timeval64))
> + {
> + __set_errno (EINVAL);
> + break;
> + }
> +
> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
> + if (! in_time_t_range (tv64->tv_sec))
> + {
> + __set_errno (EOVERFLOW);
> + break;
> + }
> +
> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
> + optname = COMPAT_SO_RCVTIMEO_OLD;
> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
> + optname = COMPAT_SO_SNDTIMEO_OLD;
> +
> + struct __timeval32 tv32 = valid_timeval64_to_timeval32
> (*tv64); +
> + r = setsockopt_syscall (fd, level, optname, &tv32, sizeof
> (tv32));
> + }
> + }
> +
> + return r;
> +}
> +#endif
> +
> +int
> +setsockopt (int fd, int level, int optname, const void *optval,
> socklen_t len) +{
> + int r = setsockopt_syscall (fd, level, optname, optval, len);
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> + if (r == -1 && errno == ENOPROTOOPT)
> + r = setsockopt32 (fd, level, optname, optval, len);
> +#endif
> +
> + return r;
> +}
> weak_alias (setsockopt, __setsockopt)
> diff --git a/sysdeps/unix/sysv/linux/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/socket-constants-time64.h new file mode
> 100644 index 0000000000..e5a3777f28
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and
> 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 20
> +#define COMPAT_SO_SNDTIMEO_OLD 21
> +#define COMPAT_SO_RCVTIMEO_NEW 66
> +#define COMPAT_SO_SNDTIMEO_NEW 67
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h new file
> mode 100644 index 0000000000..b137abdeea
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and
> 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 8192
> +#define COMPAT_SO_SNDTIMEO_OLD 16384
> +#define COMPAT_SO_RCVTIMEO_NEW 68
> +#define COMPAT_SO_SNDTIMEO_NEW 69
> +
> +#endif
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
* Adhemerval Zanella via Libc-alpha:
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +static int
> +getsockopt32 (int fd, int level, int optname, void *optval,
> + socklen_t *len)
> +{
> + int r = -1;
> +
> + if (level != SOL_SOCKET)
> + return r;
> +
> + switch (optname)
> + {
> + case COMPAT_SO_RCVTIMEO_NEW:
> + case COMPAT_SO_SNDTIMEO_NEW:
> + {
> + if (*len < sizeof (struct __timeval64))
> + {
> + __set_errno (EINVAL);
> + break;
> + }
> +
> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
> + optname = COMPAT_SO_RCVTIMEO_OLD;
> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
> + optname = COMPAT_SO_SNDTIMEO_OLD;
> +
> + struct __timeval32 tv32;
> + r = getsockopt_syscall (fd, level, optname, &tv32,
> + (socklen_t[]) { sizeof tv32 });
> + if (r < 0)
> + break;
> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
> + *tv64 = valid_timeval32_to_timeval64 (tv32);
> + *len = sizeof (*tv64);
> + }
I think POSIX requires checking the incoming *len value and truncating
the output.
Thanks,
Florian
On 20/05/2021 03:44, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
>
>> +#ifndef __ASSUME_TIME64_SYSCALLS
>> +static int
>> +getsockopt32 (int fd, int level, int optname, void *optval,
>> + socklen_t *len)
>> +{
>> + int r = -1;
>> +
>> + if (level != SOL_SOCKET)
>> + return r;
>> +
>> + switch (optname)
>> + {
>> + case COMPAT_SO_RCVTIMEO_NEW:
>> + case COMPAT_SO_SNDTIMEO_NEW:
>> + {
>> + if (*len < sizeof (struct __timeval64))
>> + {
>> + __set_errno (EINVAL);
>> + break;
>> + }
>> +
>> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
>> + optname = COMPAT_SO_RCVTIMEO_OLD;
>> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
>> + optname = COMPAT_SO_SNDTIMEO_OLD;
>> +
>> + struct __timeval32 tv32;
>> + r = getsockopt_syscall (fd, level, optname, &tv32,
>> + (socklen_t[]) { sizeof tv32 });
>> + if (r < 0)
>> + break;
>> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
>> + *tv64 = valid_timeval32_to_timeval64 (tv32);
>> + *len = sizeof (*tv64);
>> + }
>
> I think POSIX requires checking the incoming *len value and truncating
> the output.
The kernel will fail with EINVAL if the input argument is smaller than
the expected one, so I think as QoI the truncation here should mean
to return a timeout value clamped to 32 bits (instead of a truncated
memory).
* Adhemerval Zanella:
> On 20/05/2021 03:44, Florian Weimer wrote:
>> * Adhemerval Zanella via Libc-alpha:
>>
>>> +#ifndef __ASSUME_TIME64_SYSCALLS
>>> +static int
>>> +getsockopt32 (int fd, int level, int optname, void *optval,
>>> + socklen_t *len)
>>> +{
>>> + int r = -1;
>>> +
>>> + if (level != SOL_SOCKET)
>>> + return r;
>>> +
>>> + switch (optname)
>>> + {
>>> + case COMPAT_SO_RCVTIMEO_NEW:
>>> + case COMPAT_SO_SNDTIMEO_NEW:
>>> + {
>>> + if (*len < sizeof (struct __timeval64))
>>> + {
>>> + __set_errno (EINVAL);
>>> + break;
>>> + }
>>> +
>>> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
>>> + optname = COMPAT_SO_RCVTIMEO_OLD;
>>> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
>>> + optname = COMPAT_SO_SNDTIMEO_OLD;
>>> +
>>> + struct __timeval32 tv32;
>>> + r = getsockopt_syscall (fd, level, optname, &tv32,
>>> + (socklen_t[]) { sizeof tv32 });
>>> + if (r < 0)
>>> + break;
>>> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
>>> + *tv64 = valid_timeval32_to_timeval64 (tv32);
>>> + *len = sizeof (*tv64);
>>> + }
>>
>> I think POSIX requires checking the incoming *len value and truncating
>> the output.
>
> The kernel will fail with EINVAL if the input argument is smaller than
> the expected one, so I think as QoI the truncation here should mean
> to return a timeout value clamped to 32 bits (instead of a truncated
> memory).
Wow, that EINVAL behavior is not POSIX-compliant at all.
If the kernel behaves that way, I guess glibc should as well.
Thanks,
Florian
On 21/05/2021 15:37, Florian Weimer wrote:
> * Adhemerval Zanella:
>
>> On 20/05/2021 03:44, Florian Weimer wrote:
>>> * Adhemerval Zanella via Libc-alpha:
>>>
>>>> +#ifndef __ASSUME_TIME64_SYSCALLS
>>>> +static int
>>>> +getsockopt32 (int fd, int level, int optname, void *optval,
>>>> + socklen_t *len)
>>>> +{
>>>> + int r = -1;
>>>> +
>>>> + if (level != SOL_SOCKET)
>>>> + return r;
>>>> +
>>>> + switch (optname)
>>>> + {
>>>> + case COMPAT_SO_RCVTIMEO_NEW:
>>>> + case COMPAT_SO_SNDTIMEO_NEW:
>>>> + {
>>>> + if (*len < sizeof (struct __timeval64))
>>>> + {
>>>> + __set_errno (EINVAL);
>>>> + break;
>>>> + }
>>>> +
>>>> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
>>>> + optname = COMPAT_SO_RCVTIMEO_OLD;
>>>> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
>>>> + optname = COMPAT_SO_SNDTIMEO_OLD;
>>>> +
>>>> + struct __timeval32 tv32;
>>>> + r = getsockopt_syscall (fd, level, optname, &tv32,
>>>> + (socklen_t[]) { sizeof tv32 });
>>>> + if (r < 0)
>>>> + break;
>>>> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
>>>> + *tv64 = valid_timeval32_to_timeval64 (tv32);
>>>> + *len = sizeof (*tv64);
>>>> + }
>>>
>>> I think POSIX requires checking the incoming *len value and truncating
>>> the output.
>>
>> The kernel will fail with EINVAL if the input argument is smaller than
>> the expected one, so I think as QoI the truncation here should mean
>> to return a timeout value clamped to 32 bits (instead of a truncated
>> memory).
>
> Wow, that EINVAL behavior is not POSIX-compliant at all.
>
> If the kernel behaves that way, I guess glibc should as well.
In fact, checking again the source it does seem that silently truncated
the result:
net/core/sock.c
1420 case SO_RCVTIMEO_OLD:
1421 case SO_RCVTIMEO_NEW:
1422 lv = sock_get_timeout(sk->sk_rcvtimeo, &v, SO_RCVTIMEO_OLD == optname);
1423 break;
[...]
1624 if (len > lv)
1625 len = lv;
1626 if (copy_to_user(optval, &v, len))
1627 return -EFAULT;
So I think maybe we should do the same here, not sure if this is really
an improvement over compatibility.
On 5/18/21 4:55 PM, Adhemerval Zanella wrote:
> The constant values 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.
>
> It also adds an internal only SO_{RCV,SND}TIMEO where
> COMPAT_SO_{RCV,SND}TIMEO_OLD indicates pre 32 bit time support and
> COMPAT_SO_{RCV,SND}TIMEO_NEW indicates time64 support. It allows to
> refer to constant independently of the time_t ABI and kernel version
> used.
>
> Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
> kernel).
Requesting a v3 please.
This needs fixing to match POSIX requirements for silent truncation.
I agree with Florian, if POSIX requires it, and the Kernel truncates,
then we should keep doing the same. There don't seem to be much better
alternatives that don't themselves have corner cases that break existing
code that expects these behaviours.
> ---
> sysdeps/unix/sysv/linux/getsockopt.c | 67 +++++++++++++++--
> .../sysv/linux/hppa/socket-constants-time64.h | 30 ++++++++
> .../sysv/linux/mips/socket-constants-time64.h | 30 ++++++++
> .../linux/powerpc/socket-constants-time64.h | 30 ++++++++
> sysdeps/unix/sysv/linux/setsockopt.c | 71 ++++++++++++++++---
> .../unix/sysv/linux/socket-constants-time64.h | 30 ++++++++
> .../linux/sparc/socket-constants-time64.h | 30 ++++++++
> 7 files changed, 273 insertions(+), 15 deletions(-)
> create mode 100644 sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> create mode 100644 sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> create mode 100644 sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> create mode 100644 sysdeps/unix/sysv/linux/socket-constants-time64.h
> create mode 100644 sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
>
> diff --git a/sysdeps/unix/sysv/linux/getsockopt.c b/sysdeps/unix/sysv/linux/getsockopt.c
> index 76ee8a94d6..c8e502d976 100644
> --- a/sysdeps/unix/sysv/linux/getsockopt.c
> +++ b/sysdeps/unix/sysv/linux/getsockopt.c
> @@ -15,16 +15,15 @@
> License along with the GNU C Library; if not, see
> <https://www.gnu.org/licenses/>. */
>
> -#include <errno.h>
> -#include <signal.h>
> #include <sys/socket.h>
> -
> +#include <time.h>
> +#include <sysdep.h>
> #include <socketcall.h>
> -#include <kernel-features.h>
> -#include <sys/syscall.h>
> +#include <socket-constants-time64.h>
>
> -int
> -__getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
> +static int
> +getsockopt_syscall (int fd, int level, int optname, void *optval,
> + socklen_t *len)
> {
> #ifdef __ASSUME_GETSOCKOPT_SYSCALL
> return INLINE_SYSCALL (getsockopt, 5, fd, level, optname, optval, len);
> @@ -32,4 +31,58 @@ __getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
> return SOCKETCALL (getsockopt, fd, level, optname, optval, len);
> #endif
> }
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +static int
> +getsockopt32 (int fd, int level, int optname, void *optval,
> + socklen_t *len)
> +{
> + int r = -1;
> +
> + if (level != SOL_SOCKET)
> + return r;
> +
> + switch (optname)
> + {
> + case COMPAT_SO_RCVTIMEO_NEW:
> + case COMPAT_SO_SNDTIMEO_NEW:
> + {
> + if (*len < sizeof (struct __timeval64))
> + {
> + __set_errno (EINVAL);
> + break;
https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html
~~~
If the size of the option value is greater than option_len,
the value stored in the object pointed to by the option_value
argument shall be silently truncated.
~~~
> + }
> +
> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
> + optname = COMPAT_SO_RCVTIMEO_OLD;
> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
> + optname = COMPAT_SO_SNDTIMEO_OLD;
> +
> + struct __timeval32 tv32;
> + r = getsockopt_syscall (fd, level, optname, &tv32,
> + (socklen_t[]) { sizeof tv32 });
> + if (r < 0)
> + break;
> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
> + *tv64 = valid_timeval32_to_timeval64 (tv32);
> + *len = sizeof (*tv64);
> + }
> + }
> +
> + return r;
> +}
> +#endif
> +
> +int
> +__getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
> +{
> + int r = getsockopt_syscall (fd, level, optname, optval, len);
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> + if (r == -1 && errno == ENOPROTOOPT)
> + r = getsockopt32 (fd, level, optname, optval, len);
> +#endif
> +
> + return r;
> +}
> weak_alias (__getsockopt, getsockopt)
> diff --git a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> new file mode 100644
> index 0000000000..ea721e0fc2
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 4102
> +#define COMPAT_SO_SNDTIMEO_OLD 4101
> +#define COMPAT_SO_RCVTIMEO_NEW 16448
> +#define COMPAT_SO_SNDTIMEO_NEW 16449
> +
> +#endif
OK.
> diff --git a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> new file mode 100644
> index 0000000000..ab8bd62853
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 4102
> +#define COMPAT_SO_SNDTIMEO_OLD 4101
> +#define COMPAT_SO_RCVTIMEO_NEW 66
> +#define COMPAT_SO_SNDTIMEO_NEW 67
> +
> +#endif
OK.
> diff --git a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> new file mode 100644
> index 0000000000..1e48dcca8d
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 18
> +#define COMPAT_SO_SNDTIMEO_OLD 19
> +#define COMPAT_SO_RCVTIMEO_NEW 66
> +#define COMPAT_SO_SNDTIMEO_NEW 67
OK.
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/setsockopt.c b/sysdeps/unix/sysv/linux/setsockopt.c
> index 12fd7bdcde..6505202265 100644
> --- a/sysdeps/unix/sysv/linux/setsockopt.c
> +++ b/sysdeps/unix/sysv/linux/setsockopt.c
> @@ -15,21 +15,76 @@
> License along with the GNU C Library; if not, see
> <https://www.gnu.org/licenses/>. */
>
> -#include <errno.h>
> -#include <signal.h>
> #include <sys/socket.h>
> -
> +#include <time.h>
> +#include <sysdep.h>
> #include <socketcall.h>
> -#include <kernel-features.h>
> -#include <sys/syscall.h>
> +#include <socket-constants-time64.h>
>
> -int
> -setsockopt (int fd, int level, int optname, const void *optval, socklen_t len)
> +static int
> +setsockopt_syscall (int fd, int level, int optname, const void *optval,
> + socklen_t len)
> {
> #ifdef __ASSUME_SETSOCKOPT_SYSCALL
> - return INLINE_SYSCALL (setsockopt, 5, fd, level, optname, optval, len);
> + return INLINE_SYSCALL_CALL (setsockopt, fd, level, optname, optval, len);
> #else
> return SOCKETCALL (setsockopt, fd, level, optname, optval, len);
> #endif
> }
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +static int
> +setsockopt32 (int fd, int level, int optname, const void *optval,
> + socklen_t len)
> +{
> + int r = -1;
> +
> + if (level != SOL_SOCKET)
> + return r;
> +
> + switch (optname)
> + {
> + case COMPAT_SO_RCVTIMEO_NEW:
> + case COMPAT_SO_SNDTIMEO_NEW:
> + {
> + if (len < sizeof (struct __timeval64))
> + {
> + __set_errno (EINVAL);
> + break;
Same issue as above with size. Silent truncation required.
> + }
> +
> + struct __timeval64 *tv64 = (struct __timeval64 *) optval;
> + if (! in_time_t_range (tv64->tv_sec))
> + {
> + __set_errno (EOVERFLOW);
> + break;
> + }
> +
> + if (optname == COMPAT_SO_RCVTIMEO_NEW)
> + optname = COMPAT_SO_RCVTIMEO_OLD;
> + if (optname == COMPAT_SO_SNDTIMEO_NEW)
> + optname = COMPAT_SO_SNDTIMEO_OLD;
> +
> + struct __timeval32 tv32 = valid_timeval64_to_timeval32 (*tv64);
> +
> + r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32));
> + }
> + }
> +
> + return r;
> +}
> +#endif
> +
> +int
> +setsockopt (int fd, int level, int optname, const void *optval, socklen_t len)
> +{
> + int r = setsockopt_syscall (fd, level, optname, optval, len);
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> + if (r == -1 && errno == ENOPROTOOPT)
> + r = setsockopt32 (fd, level, optname, optval, len);
> +#endif
> +
> + return r;
OK.
> +}
> weak_alias (setsockopt, __setsockopt)
> diff --git a/sysdeps/unix/sysv/linux/socket-constants-time64.h b/sysdeps/unix/sysv/linux/socket-constants-time64.h
> new file mode 100644
> index 0000000000..e5a3777f28
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 20
> +#define COMPAT_SO_SNDTIMEO_OLD 21
OK. Verified 20, 21.
> +#define COMPAT_SO_RCVTIMEO_NEW 66
> +#define COMPAT_SO_SNDTIMEO_NEW 67
OK. Verified 66, 67.
> +
> +#endif
OK.
> diff --git a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> new file mode 100644
> index 0000000000..b137abdeea
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> @@ -0,0 +1,30 @@
> +/* Compat socket constants used in 64 bit compat code.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _SOCKET_CONSTANTS_TIME64_H
> +#define _SOCKET_CONSTANTS_TIME64_H
> +
> +/* The compat code requires the SO_* constants used for both 32 and 64 bit
> + time_t, however they were only added on v5.1 kernel. */
> +
> +#define COMPAT_SO_RCVTIMEO_OLD 8192
> +#define COMPAT_SO_SNDTIMEO_OLD 16384
OK. Verified 0x2000, 0x4000.
> +#define COMPAT_SO_RCVTIMEO_NEW 68
> +#define COMPAT_SO_SNDTIMEO_NEW 69
OK. Verified 0x44, 0x45.
> +
> +#endif
OK.
>
On 04/06/2021 16:30, Carlos O'Donell wrote:
>> +#ifndef __ASSUME_TIME64_SYSCALLS
>> +static int
>> +setsockopt32 (int fd, int level, int optname, const void *optval,
>> + socklen_t len)
>> +{
>> + int r = -1;
>> +
>> + if (level != SOL_SOCKET)
>> + return r;
>> +
>> + switch (optname)
>> + {
>> + case COMPAT_SO_RCVTIMEO_NEW:
>> + case COMPAT_SO_SNDTIMEO_NEW:
>> + {
>> + if (len < sizeof (struct __timeval64))
>> + {
>> + __set_errno (EINVAL);
>> + break;
>
> Same issue as above with size. Silent truncation required.
For setsockopt I think returning EINVAL is the correct approach here. POSIX
does not specify that the returned value should silently truncated in the
case of the options is larger than the input value (as for getsockopt) and
it is what kernel really does:
net/core/sock.c:
364 static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen,
365 bool old_timeval)
366 {
367 struct __kernel_sock_timeval tv;
368
369 if (old_timeval && in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
370 struct old_timeval32 tv32;
371
372 if (optlen < sizeof(tv32))
373 return -EINVAL;
374
375 if (copy_from_sockptr(&tv32, optval, sizeof(tv32)))
376 return -EFAULT;
377 tv.tv_sec = tv32.tv_sec;
378 tv.tv_usec = tv32.tv_usec;
379 } else if (old_timeval) {
380 struct __kernel_old_timeval old_tv;
381
382 if (optlen < sizeof(old_tv))
383 return -EINVAL;
384 if (copy_from_sockptr(&old_tv, optval, sizeof(old_tv)))
385 return -EFAULT;
386 tv.tv_sec = old_tv.tv_sec;
387 tv.tv_usec = old_tv.tv_usec;
388 } else {
389 if (optlen < sizeof(tv))
390 return -EINVAL;
391 if (copy_from_sockptr(&tv, optval, sizeof(tv)))
392 return -EFAULT;
393 }
@@ -15,16 +15,15 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <errno.h>
-#include <signal.h>
#include <sys/socket.h>
-
+#include <time.h>
+#include <sysdep.h>
#include <socketcall.h>
-#include <kernel-features.h>
-#include <sys/syscall.h>
+#include <socket-constants-time64.h>
-int
-__getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
+static int
+getsockopt_syscall (int fd, int level, int optname, void *optval,
+ socklen_t *len)
{
#ifdef __ASSUME_GETSOCKOPT_SYSCALL
return INLINE_SYSCALL (getsockopt, 5, fd, level, optname, optval, len);
@@ -32,4 +31,58 @@ __getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
return SOCKETCALL (getsockopt, fd, level, optname, optval, len);
#endif
}
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+static int
+getsockopt32 (int fd, int level, int optname, void *optval,
+ socklen_t *len)
+{
+ int r = -1;
+
+ if (level != SOL_SOCKET)
+ return r;
+
+ switch (optname)
+ {
+ case COMPAT_SO_RCVTIMEO_NEW:
+ case COMPAT_SO_SNDTIMEO_NEW:
+ {
+ if (*len < sizeof (struct __timeval64))
+ {
+ __set_errno (EINVAL);
+ break;
+ }
+
+ if (optname == COMPAT_SO_RCVTIMEO_NEW)
+ optname = COMPAT_SO_RCVTIMEO_OLD;
+ if (optname == COMPAT_SO_SNDTIMEO_NEW)
+ optname = COMPAT_SO_SNDTIMEO_OLD;
+
+ struct __timeval32 tv32;
+ r = getsockopt_syscall (fd, level, optname, &tv32,
+ (socklen_t[]) { sizeof tv32 });
+ if (r < 0)
+ break;
+ struct __timeval64 *tv64 = (struct __timeval64 *) optval;
+ *tv64 = valid_timeval32_to_timeval64 (tv32);
+ *len = sizeof (*tv64);
+ }
+ }
+
+ return r;
+}
+#endif
+
+int
+__getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
+{
+ int r = getsockopt_syscall (fd, level, optname, optval, len);
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+ if (r == -1 && errno == ENOPROTOOPT)
+ r = getsockopt32 (fd, level, optname, optval, len);
+#endif
+
+ return r;
+}
weak_alias (__getsockopt, getsockopt)
new file mode 100644
@@ -0,0 +1,30 @@
+/* Compat socket constants used in 64 bit compat code.
+ 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _SOCKET_CONSTANTS_TIME64_H
+#define _SOCKET_CONSTANTS_TIME64_H
+
+/* The compat code requires the SO_* constants used for both 32 and 64 bit
+ time_t, however they were only added on v5.1 kernel. */
+
+#define COMPAT_SO_RCVTIMEO_OLD 4102
+#define COMPAT_SO_SNDTIMEO_OLD 4101
+#define COMPAT_SO_RCVTIMEO_NEW 16448
+#define COMPAT_SO_SNDTIMEO_NEW 16449
+
+#endif
new file mode 100644
@@ -0,0 +1,30 @@
+/* Compat socket constants used in 64 bit compat code.
+ 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _SOCKET_CONSTANTS_TIME64_H
+#define _SOCKET_CONSTANTS_TIME64_H
+
+/* The compat code requires the SO_* constants used for both 32 and 64 bit
+ time_t, however they were only added on v5.1 kernel. */
+
+#define COMPAT_SO_RCVTIMEO_OLD 4102
+#define COMPAT_SO_SNDTIMEO_OLD 4101
+#define COMPAT_SO_RCVTIMEO_NEW 66
+#define COMPAT_SO_SNDTIMEO_NEW 67
+
+#endif
new file mode 100644
@@ -0,0 +1,30 @@
+/* Compat socket constants used in 64 bit compat code.
+ 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _SOCKET_CONSTANTS_TIME64_H
+#define _SOCKET_CONSTANTS_TIME64_H
+
+/* The compat code requires the SO_* constants used for both 32 and 64 bit
+ time_t, however they were only added on v5.1 kernel. */
+
+#define COMPAT_SO_RCVTIMEO_OLD 18
+#define COMPAT_SO_SNDTIMEO_OLD 19
+#define COMPAT_SO_RCVTIMEO_NEW 66
+#define COMPAT_SO_SNDTIMEO_NEW 67
+
+#endif
@@ -15,21 +15,76 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <errno.h>
-#include <signal.h>
#include <sys/socket.h>
-
+#include <time.h>
+#include <sysdep.h>
#include <socketcall.h>
-#include <kernel-features.h>
-#include <sys/syscall.h>
+#include <socket-constants-time64.h>
-int
-setsockopt (int fd, int level, int optname, const void *optval, socklen_t len)
+static int
+setsockopt_syscall (int fd, int level, int optname, const void *optval,
+ socklen_t len)
{
#ifdef __ASSUME_SETSOCKOPT_SYSCALL
- return INLINE_SYSCALL (setsockopt, 5, fd, level, optname, optval, len);
+ return INLINE_SYSCALL_CALL (setsockopt, fd, level, optname, optval, len);
#else
return SOCKETCALL (setsockopt, fd, level, optname, optval, len);
#endif
}
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+static int
+setsockopt32 (int fd, int level, int optname, const void *optval,
+ socklen_t len)
+{
+ int r = -1;
+
+ if (level != SOL_SOCKET)
+ return r;
+
+ switch (optname)
+ {
+ case COMPAT_SO_RCVTIMEO_NEW:
+ case COMPAT_SO_SNDTIMEO_NEW:
+ {
+ if (len < sizeof (struct __timeval64))
+ {
+ __set_errno (EINVAL);
+ break;
+ }
+
+ struct __timeval64 *tv64 = (struct __timeval64 *) optval;
+ if (! in_time_t_range (tv64->tv_sec))
+ {
+ __set_errno (EOVERFLOW);
+ break;
+ }
+
+ if (optname == COMPAT_SO_RCVTIMEO_NEW)
+ optname = COMPAT_SO_RCVTIMEO_OLD;
+ if (optname == COMPAT_SO_SNDTIMEO_NEW)
+ optname = COMPAT_SO_SNDTIMEO_OLD;
+
+ struct __timeval32 tv32 = valid_timeval64_to_timeval32 (*tv64);
+
+ r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32));
+ }
+ }
+
+ return r;
+}
+#endif
+
+int
+setsockopt (int fd, int level, int optname, const void *optval, socklen_t len)
+{
+ int r = setsockopt_syscall (fd, level, optname, optval, len);
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+ if (r == -1 && errno == ENOPROTOOPT)
+ r = setsockopt32 (fd, level, optname, optval, len);
+#endif
+
+ return r;
+}
weak_alias (setsockopt, __setsockopt)
new file mode 100644
@@ -0,0 +1,30 @@
+/* Compat socket constants used in 64 bit compat code.
+ 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _SOCKET_CONSTANTS_TIME64_H
+#define _SOCKET_CONSTANTS_TIME64_H
+
+/* The compat code requires the SO_* constants used for both 32 and 64 bit
+ time_t, however they were only added on v5.1 kernel. */
+
+#define COMPAT_SO_RCVTIMEO_OLD 20
+#define COMPAT_SO_SNDTIMEO_OLD 21
+#define COMPAT_SO_RCVTIMEO_NEW 66
+#define COMPAT_SO_SNDTIMEO_NEW 67
+
+#endif
new file mode 100644
@@ -0,0 +1,30 @@
+/* Compat socket constants used in 64 bit compat code.
+ 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
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _SOCKET_CONSTANTS_TIME64_H
+#define _SOCKET_CONSTANTS_TIME64_H
+
+/* The compat code requires the SO_* constants used for both 32 and 64 bit
+ time_t, however they were only added on v5.1 kernel. */
+
+#define COMPAT_SO_RCVTIMEO_OLD 8192
+#define COMPAT_SO_SNDTIMEO_OLD 16384
+#define COMPAT_SO_RCVTIMEO_NEW 68
+#define COMPAT_SO_SNDTIMEO_NEW 69
+
+#endif