1 /* Copyright (C) 1995-2022 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
shmid64_to_kshmid64(const struct __shmid64_ds * shmid64,struct kernel_shmid64_ds * kshmid)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
kshmid64_to_shmid64(const struct kernel_shmid64_ds * kshmid,struct __shmid64_ds * shmid64)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
shmctl_syscall(int shmid,int cmd,shmctl_arg_t * buf)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
__shmctl64(int shmid,int cmd,struct __shmid64_ds * buf)86 __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf)
87 {
88 #if __IPC_TIME64
89 struct kernel_shmid64_ds kshmid, *arg = NULL;
90 #else
91 shmctl_arg_t *arg;
92 #endif
93
94 switch (cmd)
95 {
96 case IPC_RMID:
97 case SHM_LOCK:
98 case SHM_UNLOCK:
99 arg = NULL;
100 break;
101
102 case IPC_SET:
103 case IPC_STAT:
104 case SHM_STAT:
105 case SHM_STAT_ANY:
106 #if __IPC_TIME64
107 if (buf != NULL)
108 {
109 shmid64_to_kshmid64 (buf, &kshmid);
110 arg = &kshmid;
111 }
112 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
113 if (cmd == IPC_SET)
114 arg->shm_perm.mode *= 0x10000U;
115 # endif
116 #else
117 arg = buf;
118 #endif
119 break;
120
121 case IPC_INFO:
122 case SHM_INFO:
123 /* This is a Linux extension where kernel expects either a
124 'struct shminfo' (IPC_INFO) or 'struct shm_info' (SHM_INFO). */
125 arg = (__typeof__ (arg)) buf;
126 break;
127
128 default:
129 __set_errno (EINVAL);
130 return -1;
131 }
132
133
134 int ret = shmctl_syscall (shmid, cmd, arg);
135 if (ret < 0)
136 return ret;
137
138 switch (cmd)
139 {
140 case IPC_STAT:
141 case SHM_STAT:
142 case SHM_STAT_ANY:
143 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
144 arg->shm_perm.mode >>= 16;
145 #else
146 /* Old Linux kernel versions might not clear the mode padding. */
147 if (sizeof ((struct shmid_ds){0}.shm_perm.mode)
148 != sizeof (__kernel_mode_t))
149 arg->shm_perm.mode &= 0xFFFF;
150 #endif
151
152 #if __IPC_TIME64
153 kshmid64_to_shmid64 (arg, buf);
154 #endif
155 }
156
157 return ret;
158 }
159 #if __TIMESIZE != 64
libc_hidden_def(__shmctl64)160 libc_hidden_def (__shmctl64)
161
162 static void
163 shmid_to_shmid64 (struct __shmid64_ds *shm64, const struct shmid_ds *shm)
164 {
165 shm64->shm_perm = shm->shm_perm;
166 shm64->shm_segsz = shm->shm_segsz;
167 shm64->shm_atime = shm->shm_atime
168 | ((__time64_t) shm->__shm_atime_high << 32);
169 shm64->shm_dtime = shm->shm_dtime
170 | ((__time64_t) shm->__shm_dtime_high << 32);
171 shm64->shm_ctime = shm->shm_ctime
172 | ((__time64_t) shm->__shm_ctime_high << 32);
173 shm64->shm_cpid = shm->shm_cpid;
174 shm64->shm_lpid = shm->shm_lpid;
175 shm64->shm_nattch = shm->shm_nattch;
176 }
177
178 static void
shmid64_to_shmid(struct shmid_ds * shm,const struct __shmid64_ds * shm64)179 shmid64_to_shmid (struct shmid_ds *shm, const struct __shmid64_ds *shm64)
180 {
181 shm->shm_perm = shm64->shm_perm;
182 shm->shm_segsz = shm64->shm_segsz;
183 shm->shm_atime = shm64->shm_atime;
184 shm->__shm_atime_high = 0;
185 shm->shm_dtime = shm64->shm_dtime;
186 shm->__shm_dtime_high = 0;
187 shm->shm_ctime = shm64->shm_ctime;
188 shm->__shm_ctime_high = 0;
189 shm->shm_cpid = shm64->shm_cpid;
190 shm->shm_lpid = shm64->shm_lpid;
191 shm->shm_nattch = shm64->shm_nattch;
192 }
193
194 int
__shmctl(int shmid,int cmd,struct shmid_ds * buf)195 __shmctl (int shmid, int cmd, struct shmid_ds *buf)
196 {
197 struct __shmid64_ds shmid64, *buf64 = NULL;
198 if (buf != NULL)
199 {
200 /* This is a Linux extension where kernel expects either a
201 'struct shminfo' (IPC_INFO) or 'struct shm_info' (SHM_INFO). */
202 if (cmd == IPC_INFO || cmd == SHM_INFO)
203 buf64 = (struct __shmid64_ds *) buf;
204 else
205 {
206 shmid_to_shmid64 (&shmid64, buf);
207 buf64 = &shmid64;
208 }
209 }
210
211 int ret = __shmctl64 (shmid, cmd, buf64);
212 if (ret < 0)
213 return ret;
214
215 switch (cmd)
216 {
217 case IPC_STAT:
218 case SHM_STAT:
219 case SHM_STAT_ANY:
220 shmid64_to_shmid (buf, buf64);
221 }
222
223 return ret;
224 }
225 #endif
226
227 #ifndef DEFAULT_VERSION
228 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
229 # define DEFAULT_VERSION GLIBC_2_2
230 # else
231 # define DEFAULT_VERSION GLIBC_2_31
232 # endif
233 #endif
234
235 versioned_symbol (libc, __shmctl, shmctl, DEFAULT_VERSION);
236
237 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
238 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
239 int
240 attribute_compat_text_section
__shmctl_mode16(int shmid,int cmd,struct shmid_ds * buf)241 __shmctl_mode16 (int shmid, int cmd, struct shmid_ds *buf)
242 {
243 return shmctl_syscall (shmid, cmd, (shmctl_arg_t *) buf);
244 }
245 compat_symbol (libc, __shmctl_mode16, shmctl, GLIBC_2_2);
246 #endif
247
248 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
249 struct __old_shmid_ds
250 {
251 struct __old_ipc_perm shm_perm; /* operation permission struct */
252 int shm_segsz; /* size of segment in bytes */
253 __time_t shm_atime; /* time of last shmat() */
254 __time_t shm_dtime; /* time of last shmdt() */
255 __time_t shm_ctime; /* time of last change by shmctl() */
256 __ipc_pid_t shm_cpid; /* pid of creator */
257 __ipc_pid_t shm_lpid; /* pid of last shmop */
258 unsigned short int shm_nattch; /* number of current attaches */
259 unsigned short int __shm_npages; /* size of segment (pages) */
260 unsigned long int *__shm_pages; /* array of ptrs to frames -> SHMMAX */
261 struct vm_area_struct *__attaches; /* descriptors for attaches */
262 };
263
264 int
265 attribute_compat_text_section
__old_shmctl(int shmid,int cmd,struct __old_shmid_ds * buf)266 __old_shmctl (int shmid, int cmd, struct __old_shmid_ds *buf)
267 {
268 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
269 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
270 /* For architecture that have wire-up shmctl but also have __IPC_64 to a
271 value different than default (0x0), it means the compat symbol used the
272 __NR_ipc syscall. */
273 return INLINE_SYSCALL_CALL (shmctl, shmid, cmd, buf);
274 #else
275 return INLINE_SYSCALL_CALL (ipc, IPCOP_shmctl, shmid, cmd, 0, buf);
276 #endif
277 }
278 compat_symbol (libc, __old_shmctl, shmctl, GLIBC_2_0);
279 #endif
280