1 /*
2 * Copyright (c) 2015 Andreas Schwab <schwab@suse.de>
3 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@strace.io>
4 * Copyright (c) 2015-2021 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 <string.h>
15 #include <sys/sem.h>
16
17 #include "xlat.h"
18 #include "xlat/resource_flags.h"
19
20 #ifndef SEM_STAT_ANY
21 # define SEM_STAT_ANY 20
22 #endif
23
24 #undef TEST_SEMCTL_BOGUS_CMD
25
26 /*
27 * Starting with commit glibc-2.32.9000-147-ga16d2abd496bd974a882,
28 * glibc skips semctl syscall invocations and returns EINVAL
29 * for invalid semctl commands.
30 *
31 * Apparently, this change was later backported to vendor packages, e.g.:
32 * Thu Mar 18 2021 Carlos O'Donell <carlos@redhat.com> - 2.28-153
33 * - Support SEM_STAT_ANY via semctl. Return EINVAL for unknown commands
34 * to semctl, msgctl, and shmctl. (#1912670)
35 */
36 #if GLIBC_PREREQ_GE(2, 28)
37 # define TEST_SEMCTL_BOGUS_CMD 0
38 #endif
39
40 #ifndef TEST_SEMCTL_BOGUS_CMD
41 # define TEST_SEMCTL_BOGUS_CMD 1
42 #endif
43
44 #if XLAT_RAW
45 # define str_ipc_flags "0xface1e00"
46 # define str_ipc_private "0"
47 # define str_ipc_rmid "0"
48 # define str_ipc_set "0x1"
49 # define str_ipc_stat "0x2"
50 # define str_ipc_info "0x3"
51 # define str_sem_stat "0x12"
52 # define str_sem_info "0x13"
53 # define str_sem_stat_any "0x14"
54 # define str_ipc_64 "0x100"
55 # define str_bogus_cmd "0xdeadbeef"
56 #elif XLAT_VERBOSE
57 # define str_ipc_flags \
58 "0xface1e00 /\\* IPC_CREAT\\|IPC_EXCL\\|IPC_NOWAIT\\|0xface1000 \\*/"
59 # define str_ipc_private "0 /\\* IPC_PRIVATE \\*/"
60 # define str_ipc_rmid "0 /\\* IPC_RMID \\*/"
61 # define str_ipc_set "0x1 /\\* IPC_SET \\*/"
62 # define str_ipc_stat "0x2 /\\* IPC_STAT \\*/"
63 # define str_ipc_info "0x3 /\\* IPC_INFO \\*/"
64 # define str_sem_stat "0x12 /\\* SEM_STAT \\*/"
65 # define str_sem_info "0x13 /\\* SEM_INFO \\*/"
66 # define str_sem_stat_any "0x14 /\\* SEM_STAT_ANY \\*/"
67 # define str_ipc_64 "0x100 /\\* IPC_64 \\*/"
68 # define str_bogus_cmd "0xdeadbeef /\\* SEM_\\?\\?\\? \\*/"
69 #else
70 # define str_ipc_flags "IPC_CREAT\\|IPC_EXCL\\|IPC_NOWAIT\\|0xface1000"
71 # define str_ipc_private "IPC_PRIVATE"
72 # define str_ipc_rmid "IPC_RMID"
73 # define str_ipc_set "IPC_SET"
74 # define str_ipc_stat "IPC_STAT"
75 # define str_ipc_info "IPC_INFO"
76 # define str_sem_stat "SEM_STAT"
77 # define str_sem_info "SEM_INFO"
78 # define str_sem_stat_any "SEM_STAT_ANY"
79 # define str_ipc_64 "IPC_64"
80 # define str_bogus_cmd "0xdeadbeef /\\* SEM_\\?\\?\\? \\*/"
81 #endif
82
83 union semun {
84 int val; /* Value for SETVAL */
85 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
86 unsigned short *array; /* Array for GETALL, SETALL */
87 struct seminfo *__buf; /* Buffer for IPC_INFO
88 (Linux-specific) */
89 };
90
91 static int id = -1;
92
93 static void
94 cleanup(void)
95 {
96 semctl(id, 0, IPC_RMID, 0);
97 printf("semctl\\(%d, 0, (%s\\|)?%s, \\[?NULL\\]?\\) = 0\n",
98 id, str_ipc_64, str_ipc_rmid);
99 id = -1;
100 }
101
102 static void
103 print_semid_ds(const char *const str_ipc_cmd,
104 const struct semid_ds *const ds,
105 const int rc)
106 {
107 if (rc < 0) {
108 printf("semctl\\(%d, 0, (%s\\|)?%s, (%p|\\[%p\\])\\) = %s\n",
109 id, str_ipc_64, str_ipc_cmd, &ds, &ds,
110 sprintrc_grep(rc));
111 return;
112 }
113 printf("semctl\\(%d, 0, (%s\\|)?%s, \\{sem_perm=\\{uid=%u, gid=%u"
114 ", mode=%#o, key=%u, cuid=%u, cgid=%u\\}, sem_otime=%llu"
115 ", sem_ctime=%llu, sem_nsems=%llu\\}\\) = %d\n",
116 id,
117 str_ipc_64,
118 str_ipc_cmd,
119 (unsigned) ds->sem_perm.uid,
120 (unsigned) ds->sem_perm.gid,
121 (unsigned) ds->sem_perm.mode,
122 (unsigned) ds->sem_perm.__key,
123 (unsigned) ds->sem_perm.cuid,
124 (unsigned) ds->sem_perm.cgid,
125 (unsigned long long) ds->sem_otime,
126 (unsigned long long) ds->sem_ctime,
127 (unsigned long long) ds->sem_nsems,
128 rc);
129 }
130
131 static void
132 print_sem_info(const char *const str_ipc_cmd,
133 const struct seminfo *const info,
134 const int rc)
135 {
136 if (rc < 0) {
137 printf("semctl\\(%d, 0, (%s\\|)?%s, (%p|\\[%p\\])\\) = %s\n",
138 id, str_ipc_64, str_ipc_cmd, &info, &info,
139 sprintrc_grep(rc));
140 return;
141 }
142
143 printf("semctl\\(%d, 0, (%s\\|)?%s, \\{semmap=%d, semmni=%d"
144 ", semmns=%d, semmnu=%d, semmsl=%d, semopm=%d, semume=%d"
145 ", semusz=%d, semvmx=%d, semaem=%d\\}\\) = %d\n",
146 id,
147 str_ipc_64,
148 str_ipc_cmd,
149 info->semmap,
150 info->semmni,
151 info->semmns,
152 info->semmnu,
153 info->semmsl,
154 info->semopm,
155 info->semume,
156 info->semusz,
157 info->semvmx,
158 info->semaem,
159 rc);
160 }
161
162 int
163 main(void)
164 {
165 static const key_t private_key =
166 (key_t) (0xffffffff00000000ULL | IPC_PRIVATE);
167 static const key_t bogus_key = (key_t) 0xeca86420fdb97531ULL;
168 static const int bogus_size = 0xdec0ded1;
169 static const int bogus_flags = 0xface1e55;
170 #if TEST_SEMCTL_BOGUS_CMD
171 static const int bogus_semid = 0xfdb97531;
172 static const int bogus_semnum = 0xeca86420;
173 static const int bogus_cmd = 0xdeadbeef;
174 static const unsigned long bogus_arg =
175 (unsigned long) 0xbadc0dedfffffaceULL;
176 #endif
177
178 int rc;
179 union semun un;
180 struct semid_ds ds;
181 struct seminfo info;
182
183 memset(&ds, 0, sizeof(ds));
184
185 rc = semget(bogus_key, bogus_size, bogus_flags);
186 printf("semget\\(%#llx, %d, %s\\|%#04o\\) = %s\n",
187 zero_extend_signed_to_ull(bogus_key), bogus_size,
188 str_ipc_flags, bogus_flags & 0777, sprintrc_grep(rc));
189
190 id = semget(private_key, 1, 0600);
191 if (id < 0)
192 perror_msg_and_skip("semget");
193 printf("semget\\(%s, 1, 0600\\) = %d\n", str_ipc_private, id);
194 atexit(cleanup);
195
196 #if TEST_SEMCTL_BOGUS_CMD
197 rc = semctl(bogus_semid, bogus_semnum, bogus_cmd, bogus_arg);
198 # define SEMCTL_BOGUS_ARG_FMT "(%#lx|\\[(%#lx|NULL)\\]|NULL)"
199 printf("semctl\\(%d, %d, (%s\\|)?%s, " SEMCTL_BOGUS_ARG_FMT "\\) = %s\n",
200 bogus_semid, bogus_semnum, str_ipc_64, str_bogus_cmd,
201 bogus_arg, bogus_arg, sprintrc_grep(rc));
202 #endif
203
204 un.__buf = &info;
205 rc = semctl(id, 0, IPC_INFO, un);
206 print_sem_info(str_ipc_info, &info, rc);
207
208 rc = semctl(id, 0, SEM_INFO, un);
209 print_sem_info(str_sem_info, &info, rc);
210
211 un.buf = &ds;
212 rc = semctl(id, 0, IPC_STAT, un);
213 if (rc < 0)
214 perror_msg_and_skip("semctl IPC_STAT");
215 print_semid_ds(str_ipc_stat, &ds, rc);
216
217 if (semctl(id, 0, IPC_SET, un))
218 perror_msg_and_skip("semctl IPC_SET");
219 printf("semctl\\(%d, 0, (%s\\|)?%s, \\{sem_perm=\\{uid=%u, gid=%u"
220 ", mode=%#o\\}\\}\\) = 0\n",
221 id, str_ipc_64, str_ipc_set,
222 (unsigned) ds.sem_perm.uid,
223 (unsigned) ds.sem_perm.gid,
224 (unsigned) ds.sem_perm.mode);
225
226 rc = semctl(id, 0, SEM_STAT, un);
227 print_semid_ds(str_sem_stat, &ds, rc);
228
229 /*
230 * glibc fails to pass the buffer for SEM_STAT_ANY command,
231 * so the kernel receives garbage instead of un.buf address:
232 * https://sourceware.org/bugzilla/show_bug.cgi?id=26637
233 * musl doesn't pass the buffer either.
234 */
235 #if 0
236 rc = semctl(id, 0, SEM_STAT_ANY, un);
237 print_semid_ds(str_sem_stat_any, &ds, rc);
238 #endif
239
240 return 0;
241 }