1 /* $NetBSD: t_sem.c,v 1.6 2021/12/14 16:25:11 wiz Exp $ */
2
3 /*
4 * Copyright (c) 2008, 2010, 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice(s), this list of conditions and the following disclaimer as
38 * the first lines of this file unmodified other than the possible
39 * addition of one or more copyright notices.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice(s), this list of conditions and the following disclaimer in
42 * the documentation and/or other materials provided with the
43 * distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
46 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
49 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
52 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
54 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
55 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57
58 #include <sys/cdefs.h>
59 __COPYRIGHT("@(#) Copyright (c) 2008, 2010, 2019\
60 The NetBSD Foundation, inc. All rights reserved.");
61 __RCSID("$NetBSD: t_sem.c,v 1.6 2021/12/14 16:25:11 wiz Exp $");
62
63 #include <sys/mman.h>
64 #include <sys/wait.h>
65
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <semaphore.h>
69 #include <stdio.h>
70 #include <unistd.h>
71
72 #include <atf-c.h>
73
74 #define NCHILDREN 10
75
76 ATF_TC_WITH_CLEANUP(basic);
ATF_TC_HEAD(basic,tc)77 ATF_TC_HEAD(basic, tc)
78 {
79 atf_tc_set_md_var(tc, "descr", "Checks basic functionality of POSIX "
80 "semaphores");
81 }
ATF_TC_BODY(basic,tc)82 ATF_TC_BODY(basic, tc)
83 {
84 int val;
85 sem_t *sem_b;
86
87 if (sysconf(_SC_SEMAPHORES) == -1)
88 atf_tc_skip("POSIX semaphores not supported");
89
90 sem_b = sem_open("/sem_b", O_CREAT | O_EXCL, 0644, 0);
91 ATF_REQUIRE(sem_b != SEM_FAILED);
92
93 ATF_REQUIRE_EQ(sem_getvalue(sem_b, &val), 0);
94 ATF_REQUIRE_EQ(val, 0);
95
96 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
97 ATF_REQUIRE_EQ(sem_getvalue(sem_b, &val), 0);
98 ATF_REQUIRE_EQ(val, 1);
99
100 ATF_REQUIRE_EQ(sem_wait(sem_b), 0);
101 ATF_REQUIRE_EQ(sem_trywait(sem_b), -1);
102 ATF_REQUIRE_EQ(errno, EAGAIN);
103 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
104 ATF_REQUIRE_EQ(sem_trywait(sem_b), 0);
105 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
106 ATF_REQUIRE_EQ(sem_wait(sem_b), 0);
107 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
108
109 ATF_REQUIRE_EQ(sem_close(sem_b), 0);
110 ATF_REQUIRE_EQ(sem_unlink("/sem_b"), 0);
111 }
ATF_TC_CLEANUP(basic,tc)112 ATF_TC_CLEANUP(basic, tc)
113 {
114 (void)sem_unlink("/sem_b");
115 }
116
117 ATF_TC_WITH_CLEANUP(child);
ATF_TC_HEAD(child,tc)118 ATF_TC_HEAD(child, tc)
119 {
120 atf_tc_set_md_var(tc, "descr", "Checks using semaphores to synchronize "
121 "parent with multiple child processes");
122 }
ATF_TC_BODY(child,tc)123 ATF_TC_BODY(child, tc)
124 {
125 pid_t children[NCHILDREN];
126 unsigned i, j;
127 sem_t *sem_a;
128 int status;
129
130 pid_t pid;
131
132 if (sysconf(_SC_SEMAPHORES) == -1)
133 atf_tc_skip("POSIX semaphores not supported");
134
135 sem_a = sem_open("/sem_a", O_CREAT | O_EXCL, 0644, 0);
136 ATF_REQUIRE(sem_a != SEM_FAILED);
137
138 for (j = 1; j <= 2; j++) {
139 for (i = 0; i < NCHILDREN; i++) {
140 switch ((pid = fork())) {
141 case -1:
142 atf_tc_fail("fork() returned -1");
143 case 0:
144 printf("PID %d waiting for semaphore...\n",
145 getpid());
146 ATF_REQUIRE_MSG(sem_wait(sem_a) == 0,
147 "sem_wait failed; iteration %d", j);
148 printf("PID %d got semaphore\n", getpid());
149 _exit(0);
150 default:
151 children[i] = pid;
152 break;
153 }
154 }
155
156 for (i = 0; i < NCHILDREN; i++) {
157 sleep(1);
158 printf("main loop %d: posting...\n", j);
159 ATF_REQUIRE_EQ(sem_post(sem_a), 0);
160 }
161
162 for (i = 0; i < NCHILDREN; i++) {
163 ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]);
164 ATF_REQUIRE(WIFEXITED(status));
165 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
166 }
167 }
168
169 ATF_REQUIRE_EQ(sem_close(sem_a), 0);
170 ATF_REQUIRE_EQ(sem_unlink("/sem_a"), 0);
171 }
ATF_TC_CLEANUP(child,tc)172 ATF_TC_CLEANUP(child, tc)
173 {
174 (void)sem_unlink("/sem_a");
175 }
176
177 ATF_TC_WITH_CLEANUP(pshared);
ATF_TC_HEAD(pshared,tc)178 ATF_TC_HEAD(pshared, tc)
179 {
180 atf_tc_set_md_var(tc, "descr", "Checks using pshared unnamed "
181 "semaphores to synchronize a master with multiple slave processes");
182 }
183
184 struct shared_region {
185 sem_t the_sem;
186 };
187
188 static struct shared_region *
get_shared_region(int o_flags)189 get_shared_region(int o_flags)
190 {
191
192 int fd = shm_open("/shm_semtest_a", o_flags, 0644);
193 ATF_REQUIRE(fd != -1);
194
195 ATF_REQUIRE_EQ(ftruncate(fd, sizeof(struct shared_region)), 0);
196
197 void *rv = mmap(NULL, sizeof(struct shared_region),
198 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
199 ATF_REQUIRE(rv != MAP_FAILED);
200
201 (void)close(fd);
202
203 return rv;
204 }
205
206 static void
put_shared_region(struct shared_region * r)207 put_shared_region(struct shared_region *r)
208 {
209 ATF_REQUIRE_EQ(munmap(r, sizeof(struct shared_region)), 0);
210 }
211
ATF_TC_BODY(pshared,tc)212 ATF_TC_BODY(pshared, tc)
213 {
214 struct shared_region *master_region, *slave_region;
215
216 if (sysconf(_SC_SEMAPHORES) == -1)
217 atf_tc_skip("POSIX semaphores not supported");
218
219 /*
220 * Create a shared memory region to contain the pshared
221 * semaphore, create the semaphore there, and then detach
222 * from the shared memory region to ensure that our child
223 * processes will be getting at it from scratch.
224 */
225 master_region = get_shared_region(O_RDWR | O_CREAT | O_EXCL);
226 ATF_REQUIRE(sem_init(&master_region->the_sem, 1, 0) == 0);
227 put_shared_region(master_region);
228
229 /*
230 * Now execute a test that's essentially equivalent to the
231 * "child" test above, but using the pshared semaphore in the
232 * shared memory region.
233 */
234
235 pid_t pid, children[NCHILDREN];
236 unsigned i, j;
237 int status;
238
239 for (j = 1; j <= 2; j++) {
240 for (i = 0; i < NCHILDREN; i++) {
241 switch ((pid = fork())) {
242 case -1:
243 atf_tc_fail("fork() returned -1");
244 case 0:
245 slave_region = get_shared_region(O_RDWR);
246 printf("PID %d waiting for semaphore...\n",
247 getpid());
248 ATF_REQUIRE_MSG(sem_wait(&slave_region->the_sem)
249 == 0,
250 "sem_wait failed; iteration %d", j);
251 printf("PID %d got semaphore\n", getpid());
252 _exit(0);
253 default:
254 children[i] = pid;
255 break;
256 }
257 }
258
259 master_region = get_shared_region(O_RDWR);
260
261 for (i = 0; i < NCHILDREN; i++) {
262 sleep(1);
263 printf("main loop %d: posting...\n", j);
264 ATF_REQUIRE_EQ(sem_post(&master_region->the_sem), 0);
265 }
266
267 put_shared_region(master_region);
268
269 for (i = 0; i < NCHILDREN; i++) {
270 ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]);
271 ATF_REQUIRE(WIFEXITED(status));
272 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
273 }
274 }
275
276 master_region = get_shared_region(O_RDWR);
277 ATF_REQUIRE_EQ(sem_destroy(&master_region->the_sem), 0);
278 put_shared_region(master_region);
279
280 ATF_REQUIRE_EQ(shm_unlink("/shm_semtest_a"), 0);
281 }
ATF_TC_CLEANUP(pshared,tc)282 ATF_TC_CLEANUP(pshared, tc)
283 {
284 /*
285 * The kernel will g/c the pshared semaphore when the process that
286 * created it exits, so no need to include that in the cleanup here.
287 */
288 (void)shm_unlink("/shm_semtest_a");
289 }
290
291 ATF_TC_WITH_CLEANUP(invalid_ops);
ATF_TC_HEAD(invalid_ops,tc)292 ATF_TC_HEAD(invalid_ops, tc)
293 {
294 atf_tc_set_md_var(tc, "descr", "Validates behavior when calling "
295 "bad operations for the semaphore type");
296 }
ATF_TC_BODY(invalid_ops,tc)297 ATF_TC_BODY(invalid_ops, tc)
298 {
299 sem_t *sem;
300 sem_t the_sem;
301
302 sem = sem_open("/sem_c", O_CREAT | O_EXCL, 0644, 0);
303 ATF_REQUIRE(sem != SEM_FAILED);
304 ATF_REQUIRE(sem_destroy(sem) == -1 && errno == EINVAL);
305 ATF_REQUIRE_EQ(sem_close(sem), 0);
306
307 ATF_REQUIRE_EQ(sem_init(&the_sem, 0, 0), 0);
308 ATF_REQUIRE(sem_close(&the_sem) == -1 && errno == EINVAL);
309 ATF_REQUIRE_EQ(sem_destroy(&the_sem), 0);
310 }
ATF_TC_CLEANUP(invalid_ops,tc)311 ATF_TC_CLEANUP(invalid_ops, tc)
312 {
313 (void)sem_unlink("/sem_c");
314 }
315
316 ATF_TC_WITH_CLEANUP(sem_open_address);
ATF_TC_HEAD(sem_open_address,tc)317 ATF_TC_HEAD(sem_open_address, tc)
318 {
319 atf_tc_set_md_var(tc, "descr", "Validate that multiple sem_open calls "
320 "return the same address");
321 }
ATF_TC_BODY(sem_open_address,tc)322 ATF_TC_BODY(sem_open_address, tc)
323 {
324 sem_t *sem, *sem2, *sem3;
325 atf_tc_expect_fail("kern/56549: consecutive sem_open() do not return the same address");
326 sem = sem_open("/sem_d", O_CREAT | O_EXCL, 0777, 0);
327 ATF_REQUIRE(sem != SEM_FAILED);
328 sem2 = sem_open("/sem_d", O_CREAT | O_EXCL, 0777, 0);
329 ATF_REQUIRE(sem2 == SEM_FAILED && errno == EEXIST);
330 sem3 = sem_open("/sem_d", 0);
331 ATF_REQUIRE(sem3 != SEM_FAILED);
332 ATF_REQUIRE(sem == sem3);
333 ATF_REQUIRE_EQ(sem_close(sem3), 0);
334 ATF_REQUIRE_EQ(sem_close(sem), 0);
335 ATF_REQUIRE_EQ(sem_unlink("/sem_d"), 0);
336 }
ATF_TC_CLEANUP(sem_open_address,tc)337 ATF_TC_CLEANUP(sem_open_address, tc)
338 {
339 (void)sem_unlink("/sem_d");
340 }
341
ATF_TP_ADD_TCS(tp)342 ATF_TP_ADD_TCS(tp)
343 {
344
345 ATF_TP_ADD_TC(tp, basic);
346 ATF_TP_ADD_TC(tp, child);
347 ATF_TP_ADD_TC(tp, pshared);
348 ATF_TP_ADD_TC(tp, invalid_ops);
349 ATF_TP_ADD_TC(tp, sem_open_address);
350
351 return atf_no_error();
352 }
353