y2038: linux: Provide __recvmmsg_time64 implementation

Message ID 20200408115115.8140-1-lukma@denx.de
State Superseded
Headers
Series y2038: linux: Provide __recvmmsg_time64 implementation |

Commit Message

Lukasz Majewski April 8, 2020, 11:51 a.m. UTC
  This patch provides new __recvmmsg_time64 explicit 64 bit function for
receiving messages from a socket with absolute timeout.
Moreover, a 32 bit version - __recvmmsg has been refactored to internally use
__recvmmsg_time64.

The __recvmmsg is now supposed to be used on systems still supporting 32 bit time
(__TIMESIZE != 64) - hence the necessary conversion to 64 bit struct
__timespec64 from struct timespec.

The new recvmmsg_time64 syscall available from Linux 5.1+ has been used, when
applicable.

Build tests:
- ./src/scripts/build-many-glibcs.py glibcs

Run-time tests:
- Run specific tests on ARM/x86 32bit systems (qemu):
  https://github.com/lmajewski/meta-y2038 and run tests:
  https://github.com/lmajewski/y2038-tests/commits/master

Linux kernel, headers and minimal kernel version for glibc build test matrix:
- Linux v5.1 (with recvmmsg_time64) and glibc built with v5.1 as
  minimal kernel version (--enable-kernel="5.1.0")
  The __ASSUME_TIME64_SYSCALLS flag defined.

- Linux v5.1 and default minimal kernel version
  The __ASSUME_TIME64_SYSCALLS not defined, but kernel supports recvmmsg_time64
  syscall.

- Linux v4.19 (no recvmmsg_time64 support) with default minimal kernel version for
  contemporary glibc (3.2.0)
  This kernel doesn't support recvmmsg_time64 syscall, so the fallback to recvmmsg
  is tested.

Above tests were performed with Y2038 redirection applied as well as without
(so the __TIMESIZE != 64 execution path is checked as well).
---
 include/sys/socket.h               | 10 +++++++
 sysdeps/unix/sysv/linux/recvmmsg.c | 48 ++++++++++++++++++++++++++++--
 2 files changed, 55 insertions(+), 3 deletions(-)
  

Comments

Andreas Schwab April 8, 2020, 1:47 p.m. UTC | #1
On Apr 08 2020, Lukasz Majewski wrote:

> +int
> +__recvmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags,
> +            struct timespec *tmo)
> +{
> +  struct __timespec64 ts64;
> +
> +  if (tmo != NULL)
> +     ts64 = valid_timespec_to_timespec64 (*tmo);
> +
> +  return __recvmmsg_time64 (fd, vmessages, vlen, flags,
> +                            tmo != NULL ? &ts64 : NULL);

The timeout is a r/w operand.

Andreas.
  
Lukasz Majewski April 8, 2020, 8:48 p.m. UTC | #2
Hi Andreas,

> On Apr 08 2020, Lukasz Majewski wrote:
> 
> > +int
> > +__recvmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen,
> > int flags,
> > +            struct timespec *tmo)
> > +{
> > +  struct __timespec64 ts64;
> > +
> > +  if (tmo != NULL)
> > +     ts64 = valid_timespec_to_timespec64 (*tmo);
> > +
> > +  return __recvmmsg_time64 (fd, vmessages, vlen, flags,
> > +                            tmo != NULL ? &ts64 : NULL);  
> 
> The timeout is a r/w operand.

Maybe I've wrongly looked on the manual of recvmmsg:
https://linux.die.net/man/2/recvmmsg

In this description there is no information about returning the time
left until the timeout expires.

The above documentation entry only mentions about timeout pointer being
NULL.

What do I miss?

> 
> Andreas.
> 




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
  
Andreas Schwab April 8, 2020, 9:08 p.m. UTC | #3
On Apr 08 2020, Lukasz Majewski wrote:

> What do I miss?

There is no const.

Andreas.
  
Stepan Golosunov April 9, 2020, 7:59 a.m. UTC | #4
08.04.2020 в 13:51:15 +0200 Lukasz Majewski написал:
> This patch provides new __recvmmsg_time64 explicit 64 bit function for
> receiving messages from a socket with absolute timeout.
> Moreover, a 32 bit version - __recvmmsg has been refactored to internally use
> __recvmmsg_time64.
> 
> The __recvmmsg is now supposed to be used on systems still supporting 32 bit time
> (__TIMESIZE != 64) - hence the necessary conversion to 64 bit struct
> __timespec64 from struct timespec.
> 
> The new recvmmsg_time64 syscall available from Linux 5.1+ has been used, when
> applicable.
> 
> Build tests:
> - ./src/scripts/build-many-glibcs.py glibcs
> 
> Run-time tests:
> - Run specific tests on ARM/x86 32bit systems (qemu):
>   https://github.com/lmajewski/meta-y2038 and run tests:
>   https://github.com/lmajewski/y2038-tests/commits/master
> 
> Linux kernel, headers and minimal kernel version for glibc build test matrix:
> - Linux v5.1 (with recvmmsg_time64) and glibc built with v5.1 as
>   minimal kernel version (--enable-kernel="5.1.0")
>   The __ASSUME_TIME64_SYSCALLS flag defined.
> 
> - Linux v5.1 and default minimal kernel version
>   The __ASSUME_TIME64_SYSCALLS not defined, but kernel supports recvmmsg_time64
>   syscall.
> 
> - Linux v4.19 (no recvmmsg_time64 support) with default minimal kernel version for
>   contemporary glibc (3.2.0)
>   This kernel doesn't support recvmmsg_time64 syscall, so the fallback to recvmmsg
>   is tested.
> 
> Above tests were performed with Y2038 redirection applied as well as without
> (so the __TIMESIZE != 64 execution path is checked as well).

Note that this patch is broken for m68k and s390.  And future
revisions of the patch have potential to break s390x running on
kernels older than 4.3.

>  int
> -recvmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags,
> -	  struct timespec *tmo)
> +__recvmmsg_time64 (int fd, struct mmsghdr *vmessages, unsigned int vlen,
> +                   int flags, struct __timespec64 *tmo)
>  {
>  #ifdef __ASSUME_RECVMMSG_SYSCALL
> -  return SYSCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags, tmo);
> +# ifdef __ASSUME_TIME64_SYSCALLS
> +#  ifndef __NR_recvmmsg_time64
> +#   define __NR_recvmmsg_time64 __NR_recvmmsg
> +#  endif
> +  return SYSCALL_CANCEL (recvmmsg_time64, fd, vmessages, vlen, flags, tmo);
> +# else
> +  int ret = SYSCALL_CANCEL (recvmmsg_time64, fd, vmessages, vlen, flags, tmo);
> +  if (ret == 0 || errno != ENOSYS)
> +    return ret;
> +
> +  struct timespec ts32;
> +  if (tmo != NULL)
> +    {
> +      if (! in_time_t_range (tmo->tv_sec))
> +        {
> +          __set_errno (EOVERFLOW);
> +          return -1;
> +        }
> +
> +      ts32 = valid_timespec64_to_timespec (*tmo);
> +    }
> +
> +  return SYSCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags,
> +                         tmo != NULL ? &ts32 : NULL);
> +# endif
>  #else
>    return SOCKETCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags, tmo);
>  #endif
>  }

!defined __ASSUME_RECVMMSG_SYSCALL case is broken for 32-bit
architectures.  It can be done like this:

#ifdef __ASSUME_TIME64_SYSCALLS
# ifdef __ASSUME_RECVMMSG_SYSCALL
#  ifndef __NR_recvmmsg_time64
#   define __NR_recvmmsg_time64 __NR_recvmmsg
#  endif
  SYSCALL_CANCEL (recvmmsg_time64, …
# else
  SOCKETCALL_CANCEL (recvmmsg, …
# endif
#else
  SYSCALL_CANCEL (recvmmsg_time64, …
  convert input
# ifdef __ASSUME_RECVMMSG_SYSCALL
  SYSCALL_CANCEL (recvmmsg, …
# else
  SOCKETCALL_CANCEL (recvmmsg, …
# endif
  convert output
#endif

but I think Joseph opposed to __ASSUME_TIME64_SYSCALLS being used
here (and comment near __ASSUME_TIME64_SYSCALLS does not mention
recvmmsg).

Note that there are 5 relevant cases:

1. recvmmsg syscall always exists and uses 64-bit time_t.
2. recvmmsg_time64 syscall always exists.
3. socketcall always exists and uses 64-bit time_t.
4. recvmmsg syscall with 32-bit time_t and recvmmsg_time64 exist but
one of them may be unavailable at runtime (due to old or
!COMPAT_32BIT_TIME kernel).
5. socketcall with 32-bit time_t and recvmmsg_time64 exist but one of
them may be unavailable at runtime (due to old or !COMPAT_32BIT_TIME
kernel).
  
Lukasz Majewski April 9, 2020, 11:49 a.m. UTC | #5
Hi Andreas,

> On Apr 08 2020, Lukasz Majewski wrote:
> 
> > What do I miss?  
> 
> There is no const.

Ach... right.

Just informative - in the current glibc
(./sysdeps/unix/sysv/linux/recvmmsg.c) there is no const in *tmo
pointer type qualifier (only struct timespec *tmo).

> 
> Andreas.
> 




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
  
Lukasz Majewski April 9, 2020, 1:30 p.m. UTC | #6
Hi Stepan,

> 08.04.2020 в 13:51:15 +0200 Lukasz Majewski написал:
> > This patch provides new __recvmmsg_time64 explicit 64 bit function
> > for receiving messages from a socket with absolute timeout.
> > Moreover, a 32 bit version - __recvmmsg has been refactored to
> > internally use __recvmmsg_time64.
> > 
> > The __recvmmsg is now supposed to be used on systems still
> > supporting 32 bit time (__TIMESIZE != 64) - hence the necessary
> > conversion to 64 bit struct __timespec64 from struct timespec.
> > 
> > The new recvmmsg_time64 syscall available from Linux 5.1+ has been
> > used, when applicable.
> > 
> > Build tests:
> > - ./src/scripts/build-many-glibcs.py glibcs
> > 
> > Run-time tests:
> > - Run specific tests on ARM/x86 32bit systems (qemu):
> >   https://github.com/lmajewski/meta-y2038 and run tests:
> >   https://github.com/lmajewski/y2038-tests/commits/master
> > 
> > Linux kernel, headers and minimal kernel version for glibc build
> > test matrix:
> > - Linux v5.1 (with recvmmsg_time64) and glibc built with v5.1 as
> >   minimal kernel version (--enable-kernel="5.1.0")
> >   The __ASSUME_TIME64_SYSCALLS flag defined.
> > 
> > - Linux v5.1 and default minimal kernel version
> >   The __ASSUME_TIME64_SYSCALLS not defined, but kernel supports
> > recvmmsg_time64 syscall.
> > 
> > - Linux v4.19 (no recvmmsg_time64 support) with default minimal
> > kernel version for contemporary glibc (3.2.0)
> >   This kernel doesn't support recvmmsg_time64 syscall, so the
> > fallback to recvmmsg is tested.
> > 
> > Above tests were performed with Y2038 redirection applied as well
> > as without (so the __TIMESIZE != 64 execution path is checked as
> > well).  
> 
> Note that this patch is broken for m68k and s390.  And future
> revisions of the patch have potential to break s390x running on
> kernels older than 4.3.
> 
> >  int
> > -recvmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen,
> > int flags,
> > -	  struct timespec *tmo)
> > +__recvmmsg_time64 (int fd, struct mmsghdr *vmessages, unsigned int
> > vlen,
> > +                   int flags, struct __timespec64 *tmo)
> >  {
> >  #ifdef __ASSUME_RECVMMSG_SYSCALL
> > -  return SYSCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags,
> > tmo); +# ifdef __ASSUME_TIME64_SYSCALLS
> > +#  ifndef __NR_recvmmsg_time64
> > +#   define __NR_recvmmsg_time64 __NR_recvmmsg
> > +#  endif
> > +  return SYSCALL_CANCEL (recvmmsg_time64, fd, vmessages, vlen,
> > flags, tmo); +# else
> > +  int ret = SYSCALL_CANCEL (recvmmsg_time64, fd, vmessages, vlen,
> > flags, tmo);
> > +  if (ret == 0 || errno != ENOSYS)
> > +    return ret;
> > +
> > +  struct timespec ts32;
> > +  if (tmo != NULL)
> > +    {
> > +      if (! in_time_t_range (tmo->tv_sec))
> > +        {
> > +          __set_errno (EOVERFLOW);
> > +          return -1;
> > +        }
> > +
> > +      ts32 = valid_timespec64_to_timespec (*tmo);
> > +    }
> > +
> > +  return SYSCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags,
> > +                         tmo != NULL ? &ts32 : NULL);
> > +# endif
> >  #else
> >    return SOCKETCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags,
> > tmo); #endif
> >  }  
> 
> !defined __ASSUME_RECVMMSG_SYSCALL case is broken for 32-bit
> architectures.  It can be done like this:
> 
> #ifdef __ASSUME_TIME64_SYSCALLS
> # ifdef __ASSUME_RECVMMSG_SYSCALL
> #  ifndef __NR_recvmmsg_time64
> #   define __NR_recvmmsg_time64 __NR_recvmmsg
> #  endif
>   SYSCALL_CANCEL (recvmmsg_time64, …
> # else
>   SOCKETCALL_CANCEL (recvmmsg, …
> # endif
> #else
>   SYSCALL_CANCEL (recvmmsg_time64, …
>   convert input
> # ifdef __ASSUME_RECVMMSG_SYSCALL
>   SYSCALL_CANCEL (recvmmsg, …
> # else
>   SOCKETCALL_CANCEL (recvmmsg, …
> # endif
>   convert output
> #endif
> 
> but I think Joseph opposed to __ASSUME_TIME64_SYSCALLS being used
> here (and comment near __ASSUME_TIME64_SYSCALLS does not mention
> recvmmsg).
> 
> Note that there are 5 relevant cases:
> 
> 1. recvmmsg syscall always exists and uses 64-bit time_t.
> 2. recvmmsg_time64 syscall always exists.
> 3. socketcall always exists and uses 64-bit time_t.
> 4. recvmmsg syscall with 32-bit time_t and recvmmsg_time64 exist but
> one of them may be unavailable at runtime (due to old or
> !COMPAT_32BIT_TIME kernel).
> 5. socketcall with 32-bit time_t and recvmmsg_time64 exist but one of
> them may be unavailable at runtime (due to old or !COMPAT_32BIT_TIME
> kernel).

I've grepped the sources and it seems like the
__ASSUME_RECVMMSG_SYSCALL is undefined only for m68k and s390 when the
kernel is less than 4.3 (as you pointed out above).

IMHO, there is no hurry to accept this patch and I think that it would
be better to wait for glibc minimal supported kernel is bumped above
4.3 and apply this patch then.

The same situation is with statx being always available (for all
supported archs) after 4.13 Linux.


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

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
  

Patch

diff --git a/include/sys/socket.h b/include/sys/socket.h
index 26db0e0d77..df39244005 100644
--- a/include/sys/socket.h
+++ b/include/sys/socket.h
@@ -96,6 +96,16 @@  extern ssize_t __sendmsg (int __fd, const struct msghdr *__message,
 extern int __sendmmsg (int __fd, struct mmsghdr *__vmessages,
                        unsigned int __vlen, int __flags);
 libc_hidden_proto (__sendmmsg)
+
+# include <struct___timespec64.h>
+# if __TIMESIZE == 64
+#  define __recvmmsg_time64 __recvmmsg
+# else
+extern int __recvmmsg_time64 (int fd, struct mmsghdr *vmessages,
+                              unsigned int vlen, int flags,
+                              struct __timespec64 *tmo);
+libc_hidden_proto (__recvmmsg_time64)
+# endif
 #endif
 
 /* Receive a message as described by MESSAGE from socket FD.
diff --git a/sysdeps/unix/sysv/linux/recvmmsg.c b/sysdeps/unix/sysv/linux/recvmmsg.c
index 42c80c59c3..248dae7dac 100644
--- a/sysdeps/unix/sysv/linux/recvmmsg.c
+++ b/sysdeps/unix/sysv/linux/recvmmsg.c
@@ -25,12 +25,54 @@ 
 #include <kernel-features.h>
 
 int
-recvmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags,
-	  struct timespec *tmo)
+__recvmmsg_time64 (int fd, struct mmsghdr *vmessages, unsigned int vlen,
+                   int flags, struct __timespec64 *tmo)
 {
 #ifdef __ASSUME_RECVMMSG_SYSCALL
-  return SYSCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags, tmo);
+# ifdef __ASSUME_TIME64_SYSCALLS
+#  ifndef __NR_recvmmsg_time64
+#   define __NR_recvmmsg_time64 __NR_recvmmsg
+#  endif
+  return SYSCALL_CANCEL (recvmmsg_time64, fd, vmessages, vlen, flags, tmo);
+# else
+  int ret = SYSCALL_CANCEL (recvmmsg_time64, fd, vmessages, vlen, flags, tmo);
+  if (ret == 0 || errno != ENOSYS)
+    return ret;
+
+  struct timespec ts32;
+  if (tmo != NULL)
+    {
+      if (! in_time_t_range (tmo->tv_sec))
+        {
+          __set_errno (EOVERFLOW);
+          return -1;
+        }
+
+      ts32 = valid_timespec64_to_timespec (*tmo);
+    }
+
+  return SYSCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags,
+                         tmo != NULL ? &ts32 : NULL);
+# endif
 #else
   return SOCKETCALL_CANCEL (recvmmsg, fd, vmessages, vlen, flags, tmo);
 #endif
 }
+
+#if __TIMESIZE != 64
+libc_hidden_def (__recvmmsg_time64)
+
+int
+__recvmmsg (int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags,
+            struct timespec *tmo)
+{
+  struct __timespec64 ts64;
+
+  if (tmo != NULL)
+     ts64 = valid_timespec_to_timespec64 (*tmo);
+
+  return __recvmmsg_time64 (fd, vmessages, vlen, flags,
+                            tmo != NULL ? &ts64 : NULL);
+}
+#endif
+strong_alias (__recvmmsg, recvmmsg)