(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
shmctl.c
       1  /* Copyright (C) 1995-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <sys/shm.h>
      19  #include <stdarg.h>
      20  #include <ipc_priv.h>
      21  #include <sysdep.h>
      22  #include <shlib-compat.h>
      23  #include <errno.h>
      24  #include <linux/posix_types.h>  /* For __kernel_mode_t.  */
      25  
      26  /* POSIX states ipc_perm mode should have type of mode_t.  */
      27  _Static_assert (sizeof ((struct shmid_ds){0}.shm_perm.mode)
      28  		== sizeof (mode_t),
      29  		"sizeof (shmid_ds.shm_perm.mode) != sizeof (mode_t)");
      30  
      31  #if __IPC_TIME64 == 0
      32  typedef struct shmid_ds shmctl_arg_t;
      33  #else
      34  # include <struct_kernel_shmid64_ds.h>
      35  
      36  static void
      37  shmid64_to_kshmid64 (const struct __shmid64_ds *shmid64,
      38  		     struct kernel_shmid64_ds *kshmid)
      39  {
      40    kshmid->shm_perm       = shmid64->shm_perm;
      41    kshmid->shm_segsz      = shmid64->shm_segsz;
      42    kshmid->shm_atime      = shmid64->shm_atime;
      43    kshmid->shm_atime_high = shmid64->shm_atime >> 32;
      44    kshmid->shm_dtime      = shmid64->shm_dtime;
      45    kshmid->shm_dtime_high = shmid64->shm_dtime >> 32;
      46    kshmid->shm_ctime      = shmid64->shm_ctime;
      47    kshmid->shm_ctime_high = shmid64->shm_ctime >> 32;
      48    kshmid->shm_cpid       = shmid64->shm_cpid;
      49    kshmid->shm_lpid       = shmid64->shm_lpid;
      50    kshmid->shm_nattch     = shmid64->shm_nattch;
      51  }
      52  
      53  static void
      54  kshmid64_to_shmid64 (const struct kernel_shmid64_ds *kshmid,
      55  		     struct __shmid64_ds *shmid64)
      56  {
      57    shmid64->shm_perm   = kshmid->shm_perm;
      58    shmid64->shm_segsz  = kshmid->shm_segsz;
      59    shmid64->shm_atime  = kshmid->shm_atime
      60  		        | ((__time64_t) kshmid->shm_atime_high << 32);
      61    shmid64->shm_dtime  = kshmid->shm_dtime
      62  		        | ((__time64_t) kshmid->shm_dtime_high << 32);
      63    shmid64->shm_ctime  = kshmid->shm_ctime
      64  		        | ((__time64_t) kshmid->shm_ctime_high << 32);
      65    shmid64->shm_cpid   = kshmid->shm_cpid;
      66    shmid64->shm_lpid   = kshmid->shm_lpid;
      67    shmid64->shm_nattch = kshmid->shm_nattch;
      68  }
      69  
      70  typedef struct kernel_shmid64_ds shmctl_arg_t;
      71  #endif
      72  
      73  static int
      74  shmctl_syscall (int shmid, int cmd, shmctl_arg_t *buf)
      75  {
      76  #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
      77    return INLINE_SYSCALL_CALL (shmctl, shmid, cmd | __IPC_64, buf);
      78  #else
      79    return INLINE_SYSCALL_CALL (ipc, IPCOP_shmctl, shmid, cmd | __IPC_64, 0,
      80  			      buf);
      81  #endif
      82  }
      83  
      84  /* Provide operations to control over shared memory segments.  */
      85  int
      86  __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf)
      87  {
      88  #if IPC_CTL_NEED_TRANSLATION
      89  # if __IPC_TIME64
      90    struct kernel_shmid64_ds kshmid, *arg = NULL;
      91  # else
      92    shmctl_arg_t *arg;
      93  # endif
      94  
      95    /* Some applications pass the __IPC_64 flag in cmd, to invoke
      96       previously unsupported commands back when there was no EINVAL
      97       error checking in glibc.  Mask the flag for the switch statements
      98       below.  shmctl_syscall adds back the __IPC_64 flag for the actual
      99       system call.  */
     100    cmd &= ~__IPC_64;
     101  
     102    switch (cmd)
     103      {
     104      case IPC_RMID:
     105      case SHM_LOCK:
     106      case SHM_UNLOCK:
     107        arg = NULL;
     108        break;
     109  
     110      case IPC_SET:
     111      case IPC_STAT:
     112      case SHM_STAT:
     113      case SHM_STAT_ANY:
     114  # if __IPC_TIME64
     115        if (buf != NULL)
     116  	{
     117  	  shmid64_to_kshmid64 (buf, &kshmid);
     118  	  arg = &kshmid;
     119  	}
     120  #  ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
     121        if (cmd == IPC_SET)
     122          arg->shm_perm.mode *= 0x10000U;
     123  #  endif
     124  # else
     125        arg = buf;
     126  # endif
     127        break;
     128  
     129      case IPC_INFO:
     130      case SHM_INFO:
     131        /* This is a Linux extension where kernel expects either a
     132  	 'struct shminfo' (IPC_INFO) or 'struct shm_info' (SHM_INFO).  */
     133        arg = (__typeof__ (arg)) buf;
     134        break;
     135  
     136      default:
     137        __set_errno (EINVAL);
     138        return -1;
     139      }
     140  
     141  
     142    int ret = shmctl_syscall (shmid, cmd, arg);
     143    if (ret < 0)
     144      return ret;
     145  
     146    switch (cmd)
     147      {
     148        case IPC_STAT:
     149        case SHM_STAT:
     150        case SHM_STAT_ANY:
     151  # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
     152          arg->shm_perm.mode >>= 16;
     153  # else
     154        /* Old Linux kernel versions might not clear the mode padding.  */
     155        if (sizeof ((struct shmid_ds){0}.shm_perm.mode)
     156  	  != sizeof (__kernel_mode_t))
     157  	arg->shm_perm.mode &= 0xFFFF;
     158  # endif
     159  
     160  # if __IPC_TIME64
     161        kshmid64_to_shmid64 (arg, buf);
     162  # endif
     163      }
     164  
     165    return ret;
     166  
     167  #else /* !IPC_CTL_NEED_TRANSLATION */
     168    return shmctl_syscall (shmid, cmd, buf);
     169  #endif
     170  }
     171  #if __TIMESIZE != 64
     172  libc_hidden_def (__shmctl64)
     173  
     174  static void
     175  shmid_to_shmid64 (struct __shmid64_ds *shm64, const struct shmid_ds *shm)
     176  {
     177    shm64->shm_perm   = shm->shm_perm;
     178    shm64->shm_segsz  = shm->shm_segsz;
     179    shm64->shm_atime  = shm->shm_atime
     180  		      | ((__time64_t) shm->__shm_atime_high << 32);
     181    shm64->shm_dtime  = shm->shm_dtime
     182  		      | ((__time64_t) shm->__shm_dtime_high << 32);
     183    shm64->shm_ctime  = shm->shm_ctime
     184  		      | ((__time64_t) shm->__shm_ctime_high << 32);
     185    shm64->shm_cpid   = shm->shm_cpid;
     186    shm64->shm_lpid   = shm->shm_lpid;
     187    shm64->shm_nattch = shm->shm_nattch;
     188  }
     189  
     190  static void
     191  shmid64_to_shmid (struct shmid_ds *shm, const struct __shmid64_ds *shm64)
     192  {
     193    shm->shm_perm         = shm64->shm_perm;
     194    shm->shm_segsz        = shm64->shm_segsz;
     195    shm->shm_atime        = shm64->shm_atime;
     196    shm->__shm_atime_high = 0;
     197    shm->shm_dtime        = shm64->shm_dtime;
     198    shm->__shm_dtime_high = 0;
     199    shm->shm_ctime        = shm64->shm_ctime;
     200    shm->__shm_ctime_high = 0;
     201    shm->shm_cpid         = shm64->shm_cpid;
     202    shm->shm_lpid         = shm64->shm_lpid;
     203    shm->shm_nattch       = shm64->shm_nattch;
     204  }
     205  
     206  int
     207  __shmctl (int shmid, int cmd, struct shmid_ds *buf)
     208  {
     209    struct __shmid64_ds shmid64, *buf64 = NULL;
     210    if (buf != NULL)
     211      {
     212        /* This is a Linux extension where kernel expects either a
     213  	 'struct shminfo' (IPC_INFO) or 'struct shm_info' (SHM_INFO).  */
     214        if (cmd == IPC_INFO || cmd == SHM_INFO)
     215  	buf64 = (struct __shmid64_ds *) buf;
     216        else
     217  	{
     218  	  shmid_to_shmid64 (&shmid64, buf);
     219  	  buf64 = &shmid64;
     220  	}
     221      }
     222  
     223    int ret = __shmctl64 (shmid, cmd, buf64);
     224    if (ret < 0)
     225      return ret;
     226  
     227    switch (cmd)
     228      {
     229        case IPC_STAT:
     230        case SHM_STAT:
     231        case SHM_STAT_ANY:
     232  	shmid64_to_shmid (buf, buf64);
     233      }
     234  
     235    return ret;
     236  }
     237  #endif
     238  
     239  #ifndef DEFAULT_VERSION
     240  # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
     241  #  define DEFAULT_VERSION GLIBC_2_2
     242  # else
     243  #  define DEFAULT_VERSION GLIBC_2_31
     244  # endif
     245  #endif
     246  
     247  versioned_symbol (libc, __shmctl, shmctl, DEFAULT_VERSION);
     248  
     249  #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
     250      && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
     251  int
     252  attribute_compat_text_section
     253  __shmctl_mode16 (int shmid, int cmd, struct shmid_ds *buf)
     254  {
     255    return shmctl_syscall (shmid, cmd, (shmctl_arg_t *) buf);
     256  }
     257  compat_symbol (libc, __shmctl_mode16, shmctl, GLIBC_2_2);
     258  #endif
     259  
     260  #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
     261  struct __old_shmid_ds
     262  {
     263    struct __old_ipc_perm shm_perm;	/* operation permission struct */
     264    int shm_segsz;			/* size of segment in bytes */
     265    __time_t shm_atime;			/* time of last shmat() */
     266    __time_t shm_dtime;			/* time of last shmdt() */
     267    __time_t shm_ctime;			/* time of last change by shmctl() */
     268    __ipc_pid_t shm_cpid;			/* pid of creator */
     269    __ipc_pid_t shm_lpid;			/* pid of last shmop */
     270    unsigned short int shm_nattch;	/* number of current attaches */
     271    unsigned short int __shm_npages;	/* size of segment (pages) */
     272    unsigned long int *__shm_pages;	/* array of ptrs to frames -> SHMMAX */
     273    struct vm_area_struct *__attaches;	/* descriptors for attaches */
     274  };
     275  
     276  int
     277  attribute_compat_text_section
     278  __old_shmctl (int shmid, int cmd, struct __old_shmid_ds *buf)
     279  {
     280  #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
     281      && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
     282    /* For architecture that have wire-up shmctl but also have __IPC_64 to a
     283       value different than default (0x0), it means the compat symbol used the
     284       __NR_ipc syscall.  */
     285    return INLINE_SYSCALL_CALL (shmctl, shmid, cmd, buf);
     286  #else
     287    return INLINE_SYSCALL_CALL (ipc, IPCOP_shmctl, shmid, cmd, 0, buf);
     288  #endif
     289  }
     290  compat_symbol (libc, __old_shmctl, shmctl, GLIBC_2_0);
     291  #endif