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
76ATF_TC_WITH_CLEANUP(basic);
77ATF_TC_HEAD(basic, tc)
78{
79	atf_tc_set_md_var(tc, "descr", "Checks basic functionality of POSIX "
80	    "semaphores");
81}
82ATF_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}
112ATF_TC_CLEANUP(basic, tc)
113{
114	(void)sem_unlink("/sem_b");
115}
116
117ATF_TC_WITH_CLEANUP(child);
118ATF_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}
123ATF_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}
172ATF_TC_CLEANUP(child, tc)
173{
174	(void)sem_unlink("/sem_a");
175}
176
177ATF_TC_WITH_CLEANUP(pshared);
178ATF_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
184struct shared_region {
185	sem_t	the_sem;
186};
187
188static struct shared_region *
189get_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
206static void
207put_shared_region(struct shared_region *r)
208{
209	ATF_REQUIRE_EQ(munmap(r, sizeof(struct shared_region)), 0);
210}
211
212ATF_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}
282ATF_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
291ATF_TC_WITH_CLEANUP(invalid_ops);
292ATF_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}
297ATF_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}
311ATF_TC_CLEANUP(invalid_ops, tc)
312{
313	(void)sem_unlink("/sem_c");
314}
315
316ATF_TC_WITH_CLEANUP(sem_open_address);
317ATF_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}
322ATF_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}
337ATF_TC_CLEANUP(sem_open_address, tc)
338{
339	(void)sem_unlink("/sem_d");
340}
341
342ATF_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