(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
convert_scm_timestamps.c
       1  /* Socket timestamp conversion routines.
       2     Copyright (C) 2021-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <bits/timesize.h>
      20  
      21  #if __TIMESIZE != 64
      22  # include <stdint.h>
      23  # include <string.h>
      24  # include <sys/socket.h>
      25  # include <socket-constants-time64.h>
      26  # include <libc-diag.h>
      27  
      28  /* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and
      29     appends it to the control buffer.  The 32-bit time field is kept as-is.
      30  
      31     Calls with __TIMESIZE=32 will see the converted 64-bit time control
      32     messages as spurious control message of unknown type.
      33  
      34     Calls with __TIMESIZE=64 running on pre-time64 kernels will see the
      35     original message as a spurious control ones of unknown typ while running
      36     on kernel with native 64-bit time support will only see the time64 version
      37     of the control message.  */
      38  void
      39  __convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize)
      40  {
      41    if (msg->msg_control == NULL || msg->msg_controllen == 0)
      42      return;
      43  
      44    /* The returned control message format for SO_TIMESTAMP_NEW is a
      45       'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a
      46       'struct __kernel_timespec'.  In either case it is two uint64_t
      47       members.  */
      48  
      49    /* GCC 6 issues an warning that tvts[0]/tvts[1] maybe be used uninitialized,
      50       however it would be used if type is set to a value different than 0
      51       (done by either COMPAT_SO_TIMESTAMP_OLD or COMPAT_SO_TIMESTAMPNS_OLD)
      52       which will fallthrough to 'common' label.  */
      53    DIAG_PUSH_NEEDS_COMMENT;
      54    DIAG_IGNORE_NEEDS_COMMENT (6, "-Wmaybe-uninitialized");
      55    int64_t tvts[2];
      56    DIAG_POP_NEEDS_COMMENT;
      57    int32_t tmp[2];
      58  
      59    struct cmsghdr *cmsg, *last = NULL;
      60    int type = 0;
      61  
      62    for (cmsg = CMSG_FIRSTHDR (msg);
      63         cmsg != NULL;
      64         cmsg = CMSG_NXTHDR (msg, cmsg))
      65      {
      66        last = cmsg;
      67  
      68        if (cmsg->cmsg_level != SOL_SOCKET)
      69  	continue;
      70  
      71        switch (cmsg->cmsg_type)
      72  	{
      73  	case COMPAT_SO_TIMESTAMP_OLD:
      74  	  if (type != 0)
      75  	    break;
      76  	  type = COMPAT_SO_TIMESTAMP_NEW;
      77  	  goto common;
      78  
      79  	case COMPAT_SO_TIMESTAMPNS_OLD:
      80  	  type = COMPAT_SO_TIMESTAMPNS_NEW;
      81  
      82  	/* fallthrough  */
      83  	common:
      84  	  memcpy (tmp, CMSG_DATA (cmsg), sizeof (tmp));
      85  	  tvts[0] = tmp[0];
      86  	  tvts[1] = tmp[1];
      87  	  break;
      88  	}
      89      }
      90  
      91    if (type == 0)
      92      return;
      93  
      94    if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen)
      95      {
      96        msg->msg_flags |= MSG_CTRUNC;
      97        return;
      98      }
      99  
     100    /* Zero memory for the new cmsghdr, so reading cmsg_len field
     101       by CMSG_NXTHDR does not trigger UB.  */
     102    memset (msg->msg_control + msg->msg_controllen, 0,
     103  	  CMSG_SPACE (sizeof tvts));
     104    msg->msg_controllen += CMSG_SPACE (sizeof tvts);
     105    cmsg = CMSG_NXTHDR (msg, last);
     106    cmsg->cmsg_level = SOL_SOCKET;
     107    cmsg->cmsg_type = type;
     108    cmsg->cmsg_len = CMSG_LEN (sizeof tvts);
     109    memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts);
     110  }
     111  #endif