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