1/* Copyright (C) 1995-2020 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/shm.h>
20#include <stdarg.h>
21#include <ipc_priv.h>
22#include <sysdep.h>
23#include <shlib-compat.h>
24#include <errno.h>
25#include <linux/posix_types.h> /* For __kernel_mode_t. */
26
27/* POSIX states ipc_perm mode should have type of mode_t. */
28_Static_assert (sizeof ((struct shmid_ds){0}.shm_perm.mode)
29 == sizeof (mode_t),
30 "sizeof (shmid_ds.shm_perm.mode) != sizeof (mode_t)");
31
32#if __IPC_TIME64 == 0
33typedef struct shmid_ds shmctl_arg_t;
34#else
35# include <struct_kernel_shmid64_ds.h>
36
37static void
38shmid64_to_kshmid64 (const struct __shmid64_ds *shmid64,
39 struct kernel_shmid64_ds *kshmid)
40{
41 kshmid->shm_perm = shmid64->shm_perm;
42 kshmid->shm_segsz = shmid64->shm_segsz;
43 kshmid->shm_atime = shmid64->shm_atime;
44 kshmid->shm_atime_high = shmid64->shm_atime >> 32;
45 kshmid->shm_dtime = shmid64->shm_dtime;
46 kshmid->shm_dtime_high = shmid64->shm_dtime >> 32;
47 kshmid->shm_ctime = shmid64->shm_ctime;
48 kshmid->shm_ctime_high = shmid64->shm_ctime >> 32;
49 kshmid->shm_cpid = shmid64->shm_cpid;
50 kshmid->shm_lpid = shmid64->shm_lpid;
51 kshmid->shm_nattch = shmid64->shm_nattch;
52}
53
54static void
55kshmid64_to_shmid64 (const struct kernel_shmid64_ds *kshmid,
56 struct __shmid64_ds *shmid64)
57{
58 shmid64->shm_perm = kshmid->shm_perm;
59 shmid64->shm_segsz = kshmid->shm_segsz;
60 shmid64->shm_atime = kshmid->shm_atime
61 | ((__time64_t) kshmid->shm_atime_high << 32);
62 shmid64->shm_dtime = kshmid->shm_dtime
63 | ((__time64_t) kshmid->shm_dtime_high << 32);
64 shmid64->shm_ctime = kshmid->shm_ctime
65 | ((__time64_t) kshmid->shm_ctime_high << 32);
66 shmid64->shm_cpid = kshmid->shm_cpid;
67 shmid64->shm_lpid = kshmid->shm_lpid;
68 shmid64->shm_nattch = kshmid->shm_nattch;
69}
70
71typedef struct kernel_shmid64_ds shmctl_arg_t;
72#endif
73
74static int
75shmctl_syscall (int shmid, int cmd, shmctl_arg_t *buf)
76{
77#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
78 return INLINE_SYSCALL_CALL (shmctl, shmid, cmd | __IPC_64, buf);
79#else
80 return INLINE_SYSCALL_CALL (ipc, IPCOP_shmctl, shmid, cmd | __IPC_64, 0,
81 buf);
82#endif
83}
84
85/* Provide operations to control over shared memory segments. */
86int
87__shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf)
88{
89#if __IPC_TIME64
90 struct kernel_shmid64_ds kshmid, *arg = NULL;
91 if (buf != NULL)
92 {
93 shmid64_to_kshmid64 (buf, &kshmid);
94 arg = &kshmid;
95 }
96# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
97 if (cmd == IPC_SET)
98 arg->shm_perm.mode *= 0x10000U;
99# endif
100#else
101 shmctl_arg_t *arg = buf;
102#endif
103
104 int ret = shmctl_syscall (shmid, cmd, arg);
105 if (ret < 0)
106 return ret;
107
108 switch (cmd)
109 {
110 case IPC_INFO:
111 case IPC_STAT:
112 case SHM_STAT:
113 case SHM_STAT_ANY:
114#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
115 arg->shm_perm.mode >>= 16;
116#else
117 /* Old Linux kernel versions might not clear the mode padding. */
118 if (sizeof ((struct shmid_ds){0}.shm_perm.mode)
119 != sizeof (__kernel_mode_t))
120 arg->shm_perm.mode &= 0xFFFF;
121#endif
122
123#if __IPC_TIME64
124 kshmid64_to_shmid64 (arg, buf);
125#endif
126 }
127
128 return ret;
129}
130#if __TIMESIZE != 64
131libc_hidden_def (__shmctl64)
132
133static void
134shmid_to_shmid64 (struct __shmid64_ds *shm64, const struct shmid_ds *shm)
135{
136 shm64->shm_perm = shm->shm_perm;
137 shm64->shm_segsz = shm->shm_segsz;
138 shm64->shm_atime = shm->shm_atime
139 | ((__time64_t) shm->__shm_atime_high << 32);
140 shm64->shm_dtime = shm->shm_dtime
141 | ((__time64_t) shm->__shm_dtime_high << 32);
142 shm64->shm_ctime = shm->shm_ctime
143 | ((__time64_t) shm->__shm_ctime_high << 32);
144 shm64->shm_cpid = shm->shm_cpid;
145 shm64->shm_lpid = shm->shm_lpid;
146 shm64->shm_nattch = shm->shm_nattch;
147}
148
149static void
150shmid64_to_shmid (struct shmid_ds *shm, const struct __shmid64_ds *shm64)
151{
152 shm->shm_perm = shm64->shm_perm;
153 shm->shm_segsz = shm64->shm_segsz;
154 shm->shm_atime = shm64->shm_atime;
155 shm->__shm_atime_high = 0;
156 shm->shm_dtime = shm64->shm_dtime;
157 shm->__shm_dtime_high = 0;
158 shm->shm_ctime = shm64->shm_ctime;
159 shm->__shm_ctime_high = 0;
160 shm->shm_cpid = shm64->shm_cpid;
161 shm->shm_lpid = shm64->shm_lpid;
162 shm->shm_nattch = shm64->shm_nattch;
163}
164
165int
166__shmctl (int shmid, int cmd, struct shmid_ds *buf)
167{
168 struct __shmid64_ds shmid64, *buf64 = NULL;
169 if (buf != NULL)
170 {
171 shmid_to_shmid64 (&shmid64, buf);
172 buf64 = &shmid64;
173 }
174
175 int ret = __shmctl64 (shmid, cmd, buf64);
176 if (ret < 0)
177 return ret;
178
179 switch (cmd)
180 {
181 case IPC_INFO:
182 case IPC_STAT:
183 case SHM_STAT:
184 case SHM_STAT_ANY:
185 shmid64_to_shmid (buf, buf64);
186 }
187
188 return ret;
189}
190#endif
191
192#ifndef DEFAULT_VERSION
193# ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
194# define DEFAULT_VERSION GLIBC_2_2
195# else
196# define DEFAULT_VERSION GLIBC_2_31
197# endif
198#endif
199
200versioned_symbol (libc, __shmctl, shmctl, DEFAULT_VERSION);
201
202#if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
203 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
204int
205attribute_compat_text_section
206__shmctl_mode16 (int shmid, int cmd, struct shmid_ds *buf)
207{
208 return shmctl_syscall (shmid, cmd, (shmctl_arg_t *) buf);
209}
210compat_symbol (libc, __shmctl_mode16, shmctl, GLIBC_2_2);
211#endif
212
213#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
214struct __old_shmid_ds
215{
216 struct __old_ipc_perm shm_perm; /* operation permission struct */
217 int shm_segsz; /* size of segment in bytes */
218 __time_t shm_atime; /* time of last shmat() */
219 __time_t shm_dtime; /* time of last shmdt() */
220 __time_t shm_ctime; /* time of last change by shmctl() */
221 __ipc_pid_t shm_cpid; /* pid of creator */
222 __ipc_pid_t shm_lpid; /* pid of last shmop */
223 unsigned short int shm_nattch; /* number of current attaches */
224 unsigned short int __shm_npages; /* size of segment (pages) */
225 unsigned long int *__shm_pages; /* array of ptrs to frames -> SHMMAX */
226 struct vm_area_struct *__attaches; /* descriptors for attaches */
227};
228
229int
230attribute_compat_text_section
231__old_shmctl (int shmid, int cmd, struct __old_shmid_ds *buf)
232{
233#if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
234 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
235 /* For architecture that have wire-up shmctl but also have __IPC_64 to a
236 value different than default (0x0), it means the compat symbol used the
237 __NR_ipc syscall. */
238 return INLINE_SYSCALL_CALL (shmctl, shmid, cmd, buf);
239#else
240 return INLINE_SYSCALL_CALL (ipc, IPCOP_shmctl, shmid, cmd, 0, buf);
241#endif
242}
243compat_symbol (libc, __old_shmctl, shmctl, GLIBC_2_0);
244#endif
245