(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
semctl.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/sem.h>
      19  #include <stdarg.h>
      20  #include <ipc_priv.h>
      21  #include <sysdep.h>
      22  #include <shlib-compat.h>
      23  #include <linux/posix_types.h>             /* For __kernel_mode_t.  */
      24  
      25  /* The struct used to issue the syscall.  For architectures that assume
      26     64-bit time as default (!__ASSUME_TIME64_SYSCALLS) the syscall will
      27     split the resulting 64-bit sem_{o,c}time in two fields (sem_{o,c}time
      28     and __sem_{o,c}time_high).  */
      29  union semun
      30  {
      31    int val;			/* value for SETVAL */
      32    struct semid_ds *buf;		/* buffer for IPC_STAT & IPC_SET */
      33    unsigned short int *array;	/* array for GETALL & SETALL */
      34    struct seminfo *__buf;	/* buffer for IPC_INFO */
      35  };
      36  
      37  #if __IPC_TIME64 == 0
      38  # define semun64 semun
      39  typedef union semun semctl_arg_t;
      40  #else
      41  # include <struct_kernel_semid64_ds.h>
      42  
      43  union ksemun64
      44  {
      45    int val;
      46    struct kernel_semid64_ds *buf;
      47    unsigned short int *array;
      48    struct seminfo *__buf;
      49  };
      50  
      51  # if __TIMESIZE == 64
      52  #  define semun64 semun
      53  # else
      54  /* The struct used when __semctl64 is called.  */
      55  union semun64
      56  {
      57    int val;
      58    struct __semid64_ds *buf;
      59    unsigned short int *array;
      60    struct seminfo *__buf;
      61  };
      62  # endif
      63  
      64  static void
      65  semid64_to_ksemid64 (const struct __semid64_ds *semid64,
      66  		     struct kernel_semid64_ds *ksemid)
      67  {
      68    ksemid->sem_perm       = semid64->sem_perm;
      69    ksemid->sem_otime      = semid64->sem_otime;
      70    ksemid->sem_otime_high = semid64->sem_otime >> 32;
      71    ksemid->sem_ctime      = semid64->sem_ctime;
      72    ksemid->sem_ctime_high = semid64->sem_ctime >> 32;
      73    ksemid->sem_nsems      = semid64->sem_nsems;
      74  }
      75  
      76  static void
      77  ksemid64_to_semid64 (const struct kernel_semid64_ds *ksemid,
      78  		     struct __semid64_ds *semid64)
      79  {
      80    semid64->sem_perm  = ksemid->sem_perm;
      81    semid64->sem_otime = ksemid->sem_otime
      82  		       | ((__time64_t) ksemid->sem_otime_high << 32);
      83    semid64->sem_ctime = ksemid->sem_ctime
      84  		       | ((__time64_t) ksemid->sem_ctime_high << 32);
      85    semid64->sem_nsems = ksemid->sem_nsems;
      86  }
      87  
      88  static union ksemun64
      89  semun64_to_ksemun64 (int cmd, union semun64 semun64,
      90  		     struct kernel_semid64_ds *buf)
      91  {
      92    union ksemun64 r = { 0 };
      93    switch (cmd)
      94      {
      95      case SETVAL:
      96        r.val = semun64.val;
      97        break;
      98      case GETALL:
      99      case SETALL:
     100        r.array = semun64.array;
     101        break;
     102      case SEM_STAT:
     103      case SEM_STAT_ANY:
     104      case IPC_STAT:
     105      case IPC_SET:
     106        r.buf = buf;
     107        semid64_to_ksemid64 (semun64.buf, r.buf);
     108        break;
     109      case IPC_INFO:
     110      case SEM_INFO:
     111        r.__buf = semun64.__buf;
     112        break;
     113      }
     114    return r;
     115  }
     116  
     117  typedef union ksemun64 semctl_arg_t;
     118  #endif
     119  
     120  static int
     121  semctl_syscall (int semid, int semnum, int cmd, semctl_arg_t arg)
     122  {
     123  #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
     124    return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
     125  			      arg.array);
     126  #else
     127    return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
     128  			      SEMCTL_ARG_ADDRESS (arg));
     129  #endif
     130  }
     131  
     132  /* POSIX states ipc_perm mode should have type of mode_t.  */
     133  _Static_assert (sizeof ((struct semid_ds){0}.sem_perm.mode)
     134  		== sizeof (mode_t),
     135  		"sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
     136  
     137  int
     138  __semctl64 (int semid, int semnum, int cmd, ...)
     139  {
     140    union semun64 arg64 = { 0 };
     141    va_list ap;
     142  
     143    /* Some applications pass the __IPC_64 flag in cmd, to invoke
     144       previously unsupported commands back when there was no EINVAL
     145       error checking in glibc.  Mask the flag for the switch statements
     146       below.  semctl_syscall adds back the __IPC_64 flag for the actual
     147       system call.  */
     148    cmd &= ~__IPC_64;
     149  
     150    /* Get the argument only if required.  */
     151    switch (cmd)
     152      {
     153      case SETVAL:        /* arg.val */
     154      case GETALL:        /* arg.array */
     155      case SETALL:
     156      case IPC_STAT:      /* arg.buf */
     157      case IPC_SET:
     158      case SEM_STAT:
     159      case SEM_STAT_ANY:
     160      case IPC_INFO:      /* arg.__buf */
     161      case SEM_INFO:
     162        va_start (ap, cmd);
     163        arg64 = va_arg (ap, union semun64);
     164        va_end (ap);
     165        break;
     166      case IPC_RMID:      /* arg ignored.  */
     167      case GETNCNT:
     168      case GETPID:
     169      case GETVAL:
     170      case GETZCNT:
     171        break;
     172      default:
     173        __set_errno (EINVAL);
     174        return -1;
     175      }
     176  
     177  #if __IPC_TIME64
     178    struct kernel_semid64_ds ksemid;
     179    union ksemun64 ksemun = semun64_to_ksemun64 (cmd, arg64, &ksemid);
     180  # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
     181    if (cmd == IPC_SET)
     182      ksemid.sem_perm.mode *= 0x10000U;
     183  # endif
     184    union ksemun64 arg = ksemun;
     185  #else
     186    union semun arg = arg64;
     187  #endif
     188  
     189    int ret = semctl_syscall (semid, semnum, cmd, arg);
     190    if (ret < 0)
     191      return ret;
     192  
     193    switch (cmd)
     194      {
     195      case IPC_STAT:
     196      case SEM_STAT:
     197      case SEM_STAT_ANY:
     198  #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
     199        arg.buf->sem_perm.mode >>= 16;
     200  #else
     201        /* Old Linux kernel versions might not clear the mode padding.  */
     202        if (sizeof ((struct semid_ds){0}.sem_perm.mode)
     203  	  != sizeof (__kernel_mode_t))
     204  	arg.buf->sem_perm.mode &= 0xFFFF;
     205  #endif
     206  
     207  #if __IPC_TIME64
     208        ksemid64_to_semid64 (arg.buf, arg64.buf);
     209  #endif
     210      }
     211  
     212    return ret;
     213  }
     214  #if __TIMESIZE != 64
     215  libc_hidden_def (__semctl64)
     216  
     217  
     218  /* The 64-bit time_t semid_ds version might have a different layout and
     219     internal field alignment.  */
     220  
     221  static void
     222  semid_to_semid64 (struct __semid64_ds *ds64, const struct semid_ds *ds)
     223  {
     224    ds64->sem_perm  = ds->sem_perm;
     225    ds64->sem_otime = ds->sem_otime
     226  		    | ((__time64_t) ds->__sem_otime_high << 32);
     227    ds64->sem_ctime = ds->sem_ctime
     228  		    | ((__time64_t) ds->__sem_ctime_high << 32);
     229    ds64->sem_nsems = ds->sem_nsems;
     230  }
     231  
     232  static void
     233  semid64_to_semid (struct semid_ds *ds, const struct __semid64_ds *ds64)
     234  {
     235    ds->sem_perm         = ds64->sem_perm;
     236    ds->sem_otime        = ds64->sem_otime;
     237    ds->__sem_otime_high = 0;
     238    ds->sem_ctime        = ds64->sem_ctime;
     239    ds->__sem_ctime_high = 0;
     240    ds->sem_nsems        = ds64->sem_nsems;
     241  }
     242  
     243  static union semun64
     244  semun_to_semun64 (int cmd, union semun semun, struct __semid64_ds *semid64)
     245  {
     246    union semun64 r = { 0 };
     247    switch (cmd)
     248      {
     249      case SETVAL:
     250        r.val = semun.val;
     251        break;
     252      case GETALL:
     253      case SETALL:
     254        r.array = semun.array;
     255        break;
     256      case SEM_STAT:
     257      case SEM_STAT_ANY:
     258      case IPC_STAT:
     259      case IPC_SET:
     260        r.buf = semid64;
     261        semid_to_semid64 (r.buf, semun.buf);
     262        break;
     263      case IPC_INFO:
     264      case SEM_INFO:
     265        r.__buf = semun.__buf;
     266        break;
     267      }
     268    return r;
     269  }
     270  
     271  int
     272  __semctl (int semid, int semnum, int cmd, ...)
     273  {
     274    union semun arg = { 0 };
     275  
     276    va_list ap;
     277  
     278    /* Get the argument only if required.  */
     279    switch (cmd)
     280      {
     281      case SETVAL:        /* arg.val */
     282      case GETALL:        /* arg.array */
     283      case SETALL:
     284      case IPC_STAT:      /* arg.buf */
     285      case IPC_SET:
     286      case SEM_STAT:
     287      case SEM_STAT_ANY:
     288      case IPC_INFO:      /* arg.__buf */
     289      case SEM_INFO:
     290        va_start (ap, cmd);
     291        arg = va_arg (ap, union semun);
     292        va_end (ap);
     293        break;
     294      /* __semctl64 handles non-supported commands.  */
     295      }
     296  
     297    struct __semid64_ds semid64;
     298    union semun64 arg64 = semun_to_semun64 (cmd, arg, &semid64);
     299  
     300    int ret = __semctl64 (semid, semnum, cmd, arg64);
     301    if (ret < 0)
     302      return ret;
     303  
     304    switch (cmd)
     305      {
     306      case IPC_STAT:
     307      case SEM_STAT:
     308      case SEM_STAT_ANY:
     309        semid64_to_semid (arg.buf, arg64.buf);
     310      }
     311  
     312    return ret;
     313  }
     314  #endif
     315  
     316  #ifndef DEFAULT_VERSION
     317  # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
     318  #  define DEFAULT_VERSION GLIBC_2_2
     319  # else
     320  #  define DEFAULT_VERSION GLIBC_2_31
     321  # endif
     322  #endif
     323  versioned_symbol (libc, __semctl, semctl, DEFAULT_VERSION);
     324  
     325  #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
     326      && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
     327  int
     328  attribute_compat_text_section
     329  __semctl_mode16 (int semid, int semnum, int cmd, ...)
     330  {
     331    semctl_arg_t arg = { 0 };
     332    va_list ap;
     333  
     334    /* Get the argument only if required.  */
     335    switch (cmd)
     336      {
     337      case SETVAL:        /* arg.val */
     338      case GETALL:        /* arg.array */
     339      case SETALL:
     340      case IPC_STAT:      /* arg.buf */
     341      case IPC_SET:
     342      case SEM_STAT:
     343      case SEM_STAT_ANY:
     344      case IPC_INFO:      /* arg.__buf */
     345      case SEM_INFO:
     346        va_start (ap, cmd);
     347        arg = va_arg (ap, semctl_arg_t);
     348        va_end (ap);
     349        break;
     350      }
     351  
     352    return semctl_syscall (semid, semnum, cmd, arg);
     353  }
     354  compat_symbol (libc, __semctl_mode16, semctl, GLIBC_2_2);
     355  #endif
     356  
     357  #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
     358  /* Since semctl use a variadic argument for semid_ds there is not need to
     359     define and tie the compatibility symbol to the old 'union semun'
     360     definition.  */
     361  int
     362  attribute_compat_text_section
     363  __old_semctl (int semid, int semnum, int cmd, ...)
     364  {
     365    union semun arg = { 0 };
     366    va_list ap;
     367  
     368    /* Get the argument only if required.  */
     369    switch (cmd)
     370      {
     371      case SETVAL:        /* arg.val */
     372      case GETALL:        /* arg.array */
     373      case SETALL:
     374      case IPC_STAT:      /* arg.buf */
     375      case IPC_SET:
     376      case SEM_STAT:
     377      case SEM_STAT_ANY:
     378      case IPC_INFO:      /* arg.__buf */
     379      case SEM_INFO:
     380        va_start (ap, cmd);
     381        arg = va_arg (ap, union semun);
     382        va_end (ap);
     383        break;
     384      }
     385  
     386  #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
     387      && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
     388   /* For architectures that have wire-up semctl but also have __IPC_64 to a
     389      value different than default (0x0) it means the compat symbol used the
     390      __NR_ipc syscall.  */
     391    return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd, arg.array);
     392  # else
     393    return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd,
     394  			      SEMCTL_ARG_ADDRESS (arg));
     395  # endif
     396  }
     397  compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
     398  #endif