1 /*
2 * Copyright (c) 2015 Elvira Khabirova <lineprinter0@gmail.com>
3 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@strace.io>
4 * Copyright (c) 2015-2020 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/shm.h>
15
16 #ifndef SHM_HUGE_SHIFT
17 # define SHM_HUGE_SHIFT 26
18 #endif
19
20 #ifndef SHM_HUGE_MASK
21 # define SHM_HUGE_MASK 0x3f
22 #endif
23
24 #ifndef SHM_STAT_ANY
25 # define SHM_STAT_ANY 15
26 #endif
27
28 #ifndef SHM_NORESERVE
29 # define SHM_NORESERVE 010000
30 #endif
31
32 #undef TEST_SHMCTL_BOGUS_ADDR
33 #undef TEST_SHMCTL_BOGUS_CMD
34
35 /*
36 * Starting with commit glibc-2.32~80, on every 32-bit architecture
37 * where 32-bit time_t support is enabled, glibc tries to retrieve
38 * the data provided in the third argument of shmctl call.
39 */
40 #if GLIBC_PREREQ_GE(2, 32) && defined __TIMESIZE && __TIMESIZE != 64
41 # define TEST_SHMCTL_BOGUS_ADDR 0
42 #endif
43
44 /*
45 * Starting with commit glibc-2.32.9000-207-g9ebaabeaac1a96b0d91f,
46 * glibc skips shmctl syscall invocations and returns EINVAL
47 * for invalid shmctl commands.
48 */
49 #if GLIBC_PREREQ_GE(2, 32)
50 # define TEST_SHMCTL_BOGUS_CMD 0
51 #endif
52
53 #ifndef TEST_SHMCTL_BOGUS_ADDR
54 # define TEST_SHMCTL_BOGUS_ADDR 1
55 #endif
56 #ifndef TEST_SHMCTL_BOGUS_CMD
57 # define TEST_SHMCTL_BOGUS_CMD 1
58 #endif
59
60 #include "xlat.h"
61 #include "xlat/shm_resource_flags.h"
62
63 #if XLAT_RAW
64 # define str_ipc_flags "0x2ce1e00"
65 # define str_shm_huge "21<<26"
66 # define str_ipc_private "0"
67 # define str_ipc_rmid "0"
68 # define str_ipc_set "0x1"
69 # define str_ipc_stat "0x2"
70 # define str_ipc_info "0x3"
71 # define str_shm_stat "0xd"
72 # define str_shm_info "0xe"
73 # define str_shm_stat_any "0xf"
74 # define str_ipc_64 "0x100"
75 # define str_bogus_cmd "0xdefaced2"
76 #elif XLAT_VERBOSE
77 # define str_ipc_flags \
78 "0x2ce1e00 /\\* IPC_CREAT\\|IPC_EXCL\\|SHM_HUGETLB\\|SHM_NORESERVE" \
79 "\\|0x2ce0000 \\*/"
80 # define str_shm_huge "21<<26 /\\* SHM_HUGE_SHIFT \\*/"
81 # define str_ipc_private "0 /\\* IPC_PRIVATE \\*/"
82 # define str_ipc_rmid "0 /\\* IPC_RMID \\*/"
83 # define str_ipc_set "0x1 /\\* IPC_SET \\*/"
84 # define str_ipc_stat "0x2 /\\* IPC_STAT \\*/"
85 # define str_ipc_info "0x3 /\\* IPC_INFO \\*/"
86 # define str_shm_stat "0xd /\\* SHM_STAT \\*/"
87 # define str_shm_info "0xe /\\* SHM_INFO \\*/"
88 # define str_shm_stat_any "0xf /\\* SHM_STAT_ANY \\*/"
89 # define str_ipc_64 "0x100 /\\* IPC_64 \\*/"
90 # define str_bogus_cmd "0xdefaced2 /\\* SHM_\\?\\?\\? \\*/"
91 #else
92 # define str_ipc_flags \
93 "IPC_CREAT\\|IPC_EXCL\\|SHM_HUGETLB\\|SHM_NORESERVE\\|0x2ce0000"
94 # define str_shm_huge "21<<SHM_HUGE_SHIFT"
95 # define str_ipc_private "IPC_PRIVATE"
96 # define str_ipc_rmid "IPC_RMID"
97 # define str_ipc_set "IPC_SET"
98 # define str_ipc_stat "IPC_STAT"
99 # define str_ipc_info "IPC_INFO"
100 # define str_shm_stat "SHM_STAT"
101 # define str_shm_info "SHM_INFO"
102 # define str_shm_stat_any "SHM_STAT_ANY"
103 # define str_ipc_64 "IPC_64"
104 # define str_bogus_cmd "0xdefaced2 /\\* SHM_\\?\\?\\? \\*/"
105 #endif
106
107 static int id = -1;
108
109 static void
110 cleanup(void)
111 {
112 shmctl(id, IPC_RMID, NULL);
113 printf("shmctl\\(%d, (%s\\|)?%s, NULL\\) = 0\n",
114 id, str_ipc_64, str_ipc_rmid);
115 id = -1;
116 }
117
118 static void
119 print_shmid_ds(const char *const str_ipc_cmd,
120 const struct shmid_ds *const ds,
121 const int rc)
122 {
123 if (rc < 0) {
124 printf("shmctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
125 id, str_ipc_64, str_ipc_cmd, ds, sprintrc_grep(rc));
126 return;
127 }
128 printf("shmctl\\(%d, (%s\\|)?%s, \\{shm_perm=\\{uid=%u, gid=%u"
129 ", mode=%#o, key=%u, cuid=%u, cgid=%u\\}, shm_segsz=%u"
130 ", shm_cpid=%d, shm_lpid=%d, shm_nattch=%u, shm_atime=%u"
131 ", shm_dtime=%u, shm_ctime=%u\\}\\) = %d\n",
132 id,
133 str_ipc_64,
134 str_ipc_cmd,
135 (unsigned) ds->shm_perm.uid,
136 (unsigned) ds->shm_perm.gid,
137 (unsigned) ds->shm_perm.mode,
138 (unsigned) ds->shm_perm.__key,
139 (unsigned) ds->shm_perm.cuid,
140 (unsigned) ds->shm_perm.cgid,
141 (unsigned) ds->shm_segsz,
142 (int) ds->shm_cpid,
143 (int) ds->shm_lpid,
144 (unsigned) ds->shm_nattch,
145 (unsigned) ds->shm_atime,
146 (unsigned) ds->shm_dtime,
147 (unsigned) ds->shm_ctime,
148 rc);
149 }
150
151 static void
152 print_ipc_info(const char *const str_ipc_cmd,
153 const struct shminfo *const info,
154 const int rc)
155 {
156 if (rc < 0) {
157 printf("shmctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
158 id, str_ipc_64, str_ipc_cmd, info, sprintrc_grep(rc));
159 return;
160 }
161
162 printf("shmctl\\(%d, (%s\\|)?%s, \\{shmmax=%llu, shmmin=%llu"
163 ", shmmni=%llu, shmseg=%llu, shmall=%llu\\}\\) = %d\n",
164 id,
165 str_ipc_64,
166 str_ipc_cmd,
167 (unsigned long long) info->shmmax,
168 (unsigned long long) info->shmmin,
169 (unsigned long long) info->shmmni,
170 (unsigned long long) info->shmseg,
171 (unsigned long long) info->shmall,
172 rc);
173 }
174
175 static void
176 print_shm_info(const char *const str_ipc_cmd,
177 const struct shm_info *const info,
178 const int rc)
179 {
180 if (rc < 0) {
181 printf("shmctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
182 id, str_ipc_64, str_ipc_cmd, info, sprintrc_grep(rc));
183 return;
184 }
185
186 printf("shmctl\\(%d, (%s\\|)?%s, \\{used_ids=%d, shm_tot=%llu"
187 ", shm_rss=%llu, shm_swp=%llu, swap_attempts=%llu"
188 ", swap_successes=%llu\\}\\) = %d\n",
189 id,
190 str_ipc_64,
191 str_ipc_cmd,
192 info->used_ids,
193 (unsigned long long) info->shm_tot,
194 (unsigned long long) info->shm_rss,
195 (unsigned long long) info->shm_swp,
196 (unsigned long long) info->swap_attempts,
197 (unsigned long long) info->swap_successes,
198 rc);
199 }
200
201 int
202 main(void)
203 {
204 static const key_t private_key =
205 (key_t) (0xffffffff00000000ULL | IPC_PRIVATE);
206 static const key_t bogus_key = (key_t) 0xeca86420fdb97531ULL;
207 #if TEST_SHMCTL_BOGUS_CMD || TEST_SHMCTL_BOGUS_ADDR
208 static const int bogus_id = 0xdefaced1;
209 #endif
210 #if TEST_SHMCTL_BOGUS_CMD
211 static const int bogus_cmd = 0xdefaced2;
212 #endif
213 #if TEST_SHMCTL_BOGUS_ADDR
214 static void * const bogus_addr = (void *) -1L;
215 #endif
216 static const size_t bogus_size =
217 /*
218 * musl sets size to SIZE_MAX if size argument is greater than
219 * PTRDIFF_MAX - musl/src/ipc/shmget.c
220 */
221 #ifdef __GLIBC__
222 (size_t) 0xdec0ded1dec0ded2ULL;
223 #else
224 (size_t) 0x1e55c0de5dec0dedULL;
225 #endif
226 static const unsigned int bogus_ipc_shm_flags =
227 IPC_CREAT | IPC_EXCL | SHM_HUGETLB | SHM_NORESERVE;
228 static const unsigned int huge_mask = SHM_HUGE_MASK << SHM_HUGE_SHIFT;
229 static const unsigned int huge_flags = 21 << SHM_HUGE_SHIFT;
230 int bogus_flags;
231 int rc;
232 union {
233 struct shmid_ds ds;
234 struct shminfo ipc_info;
235 struct shm_info shm_info;
236 } buf;
237
238 rc = shmget(bogus_key, bogus_size, 0);
239 printf("shmget\\(%#llx, %zu, 000\\) = %s\n",
240 zero_extend_signed_to_ull(bogus_key), bogus_size,
241 sprintrc_grep(rc));
242
243 rc = shmget(bogus_key, bogus_size, huge_flags);
244 printf("shmget\\(%#llx, %zu, %s\\|%#03o\\) = %s\n",
245 zero_extend_signed_to_ull(bogus_key), bogus_size,
246 str_shm_huge, 0, sprintrc_grep(rc));
247
248 bogus_flags = 0xface1e55 & ~(bogus_ipc_shm_flags | huge_mask);
249 rc = shmget(bogus_key, bogus_size, bogus_flags);
250 printf("shmget\\(%#llx, %zu, %#x\\|%#03o\\) = %s\n",
251 zero_extend_signed_to_ull(bogus_key), bogus_size,
252 bogus_flags & ~0777,
253 bogus_flags & 0777, sprintrc_grep(rc));
254
255 bogus_flags |= bogus_ipc_shm_flags;
256 rc = shmget(bogus_key, bogus_size, bogus_flags);
257 printf("shmget\\(%#llx, %zu, %s\\|%#03o\\) = %s\n",
258 zero_extend_signed_to_ull(bogus_key), bogus_size,
259 str_ipc_flags,
260 bogus_flags & 0777, sprintrc_grep(rc));
261
262 bogus_flags |= huge_flags;
263 rc = shmget(bogus_key, bogus_size, bogus_flags);
264 printf("shmget\\(%#llx, %zu, %s\\|%s\\|%#03o\\) = %s\n",
265 zero_extend_signed_to_ull(bogus_key), bogus_size,
266 str_ipc_flags, str_shm_huge,
267 bogus_flags & 0777, sprintrc_grep(rc));
268
269 bogus_flags &= ~bogus_ipc_shm_flags;
270 rc = shmget(bogus_key, bogus_size, bogus_flags);
271 printf("shmget\\(%#llx, %zu, %#x\\|%s\\|%#03o\\) = %s\n",
272 zero_extend_signed_to_ull(bogus_key), bogus_size,
273 bogus_flags & ~(0777 | huge_mask),
274 str_shm_huge,
275 bogus_flags & 0777, sprintrc_grep(rc));
276
277 id = shmget(private_key, 1, 0600);
278 if (id < 0)
279 perror_msg_and_skip("shmget");
280 printf("shmget\\(%s, 1, 0600\\) = %d\n", str_ipc_private, id);
281 atexit(cleanup);
282
283 #if TEST_SHMCTL_BOGUS_CMD
284 rc = shmctl(bogus_id, bogus_cmd, NULL);
285 printf("shmctl\\(%d, (%s\\|)?%s, NULL\\) = %s\n",
286 bogus_id, str_ipc_64, str_bogus_cmd, sprintrc_grep(rc));
287 #endif
288
289 #if TEST_SHMCTL_BOGUS_ADDR
290 rc = shmctl(bogus_id, IPC_STAT, bogus_addr);
291 printf("shmctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
292 bogus_id, str_ipc_64, str_ipc_stat, bogus_addr,
293 sprintrc_grep(rc));
294 #endif
295
296 rc = shmctl(id, IPC_STAT, &buf.ds);
297 if (rc < 0)
298 perror_msg_and_skip("shmctl IPC_STAT");
299 print_shmid_ds(str_ipc_stat, &buf.ds, rc);
300
301 if (shmctl(id, IPC_SET, &buf.ds))
302 perror_msg_and_skip("shmctl IPC_SET");
303 printf("shmctl\\(%d, (%s\\|)?%s, \\{shm_perm=\\{uid=%u, gid=%u"
304 ", mode=%#o\\}\\}\\) = 0\n",
305 id, str_ipc_64, str_ipc_set,
306 (unsigned) buf.ds.shm_perm.uid,
307 (unsigned) buf.ds.shm_perm.gid,
308 (unsigned) buf.ds.shm_perm.mode);
309
310 rc = shmctl(id, IPC_INFO, &buf.ds);
311 print_ipc_info(str_ipc_info, &buf.ipc_info, rc);
312
313 rc = shmctl(id, SHM_INFO, &buf.ds);
314 print_shm_info(str_shm_info, &buf.shm_info, rc);
315
316 rc = shmctl(id, SHM_STAT, &buf.ds);
317 print_shmid_ds(str_shm_stat, &buf.ds, rc);
318
319 rc = shmctl(id, SHM_STAT_ANY, &buf.ds);
320 print_shmid_ds(str_shm_stat_any, &buf.ds, rc);
321
322 return 0;
323 }