t_msgsnd.c revision 319094
1/* $NetBSD: t_msgsnd.c,v 1.3 2017/01/13 20:44:45 christos Exp $ */
2
3/*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jukka Ruohonen.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include <sys/cdefs.h>
32__RCSID("$NetBSD: t_msgsnd.c,v 1.3 2017/01/13 20:44:45 christos Exp $");
33
34#include <sys/msg.h>
35#include <sys/stat.h>
36#include <sys/sysctl.h>
37#include <sys/wait.h>
38
39#include <atf-c.h>
40#include <errno.h>
41#include <limits.h>
42#include <pwd.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <sysexits.h>
48#include <time.h>
49#include <unistd.h>
50
51#define MSG_KEY		1234
52#define MSG_MTYPE_1	0x41
53#define	MSG_MTYPE_2	0x42
54#define MSG_MTYPE_3	0x43
55
56struct msg {
57	long		 mtype;
58	char		 buf[3];
59};
60
61static void		clean(void);
62
63static void
64clean(void)
65{
66	int id;
67
68	if ((id = msgget(MSG_KEY, 0)) != -1)
69		(void)msgctl(id, IPC_RMID, 0);
70}
71
72ATF_TC_WITH_CLEANUP(msgsnd_block);
73ATF_TC_HEAD(msgsnd_block, tc)
74{
75	atf_tc_set_md_var(tc, "descr", "Test that msgsnd(2) blocks");
76	atf_tc_set_md_var(tc, "timeout", "10");
77}
78
79ATF_TC_BODY(msgsnd_block, tc)
80{
81	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
82	int id, sta;
83	pid_t pid;
84
85	id = msgget(MSG_KEY, IPC_CREAT | 0600);
86	ATF_REQUIRE(id != -1);
87
88	pid = fork();
89	ATF_REQUIRE(pid >= 0);
90
91	if (pid == 0) {
92
93		/*
94		 * Enqueue messages until some limit (e.g. the maximum
95		 * number of messages in the queue or the maximum number
96		 * of bytes in the queue) is reached. After this the call
97		 * should block when the IPC_NOWAIT is not set.
98		 */
99		for (;;) {
100
101#ifdef __FreeBSD__
102			if (msgsnd(id, &msg, sizeof(msg.buf), 0) < 0)
103#else
104			if (msgsnd(id, &msg, sizeof(struct msg), 0) < 0)
105#endif
106				_exit(EXIT_FAILURE);
107		}
108	}
109
110	(void)sleep(2);
111	(void)kill(pid, SIGKILL);
112	(void)wait(&sta);
113
114	if (WIFEXITED(sta) != 0 || WIFSIGNALED(sta) == 0)
115		atf_tc_fail("msgsnd(2) did not block");
116
117	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
118}
119
120ATF_TC_CLEANUP(msgsnd_block, tc)
121{
122	clean();
123}
124
125ATF_TC_WITH_CLEANUP(msgsnd_count);
126ATF_TC_HEAD(msgsnd_count, tc)
127{
128	atf_tc_set_md_var(tc, "descr",
129	    "Test that msgsnd(2) increments the amount of "
130	    "message in the queue, as given by msgctl(2)");
131	atf_tc_set_md_var(tc, "timeout", "10");
132}
133
134ATF_TC_BODY(msgsnd_count, tc)
135{
136	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
137	struct msqid_ds ds;
138	size_t i = 0;
139	int id, rv;
140
141	id = msgget(MSG_KEY, IPC_CREAT | 0600);
142	ATF_REQUIRE(id != -1);
143
144	for (;;) {
145
146		errno = 0;
147#ifdef	__FreeBSD__
148		rv = msgsnd(id, &msg, sizeof(msg.buf), IPC_NOWAIT);
149#else
150		rv = msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
151#endif
152
153		if (rv == 0) {
154			i++;
155			continue;
156		}
157
158		if (rv == -1 && errno == EAGAIN)
159			break;
160
161		atf_tc_fail("failed to enqueue a message");
162	}
163
164	(void)memset(&ds, 0, sizeof(struct msqid_ds));
165	(void)msgctl(id, IPC_STAT, &ds);
166
167	if (ds.msg_qnum != i)
168		atf_tc_fail("incorrect message count");
169
170	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
171}
172
173ATF_TC_CLEANUP(msgsnd_count, tc)
174{
175	clean();
176}
177
178ATF_TC_WITH_CLEANUP(msgsnd_err);
179ATF_TC_HEAD(msgsnd_err, tc)
180{
181	atf_tc_set_md_var(tc, "descr", "Test errors from msgsnd(2)");
182}
183
184ATF_TC_BODY(msgsnd_err, tc)
185{
186	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
187	int id;
188
189	id = msgget(MSG_KEY, IPC_CREAT | 0600);
190	ATF_REQUIRE(id != -1);
191
192	errno = 0;
193
194	ATF_REQUIRE_ERRNO(EFAULT, msgsnd(id, (void *)-1,
195#ifdef	__FreeBSD__
196		sizeof(msg.buf), IPC_NOWAIT) == -1);
197#else
198		sizeof(struct msg), IPC_NOWAIT) == -1);
199#endif
200
201	errno = 0;
202
203	ATF_REQUIRE_ERRNO(EINVAL, msgsnd(-1, &msg,
204#ifdef	__FreeBSD__
205		sizeof(msg.buf), IPC_NOWAIT) == -1);
206#else
207		sizeof(struct msg), IPC_NOWAIT) == -1);
208#endif
209
210	errno = 0;
211
212	ATF_REQUIRE_ERRNO(EINVAL, msgsnd(-1, &msg,
213		SSIZE_MAX, IPC_NOWAIT) == -1);
214
215	errno = 0;
216	msg.mtype = 0;
217
218	ATF_REQUIRE_ERRNO(EINVAL, msgsnd(id, &msg,
219#ifdef	__FreeBSD__
220		sizeof(msg.buf), IPC_NOWAIT) == -1);
221#else
222		sizeof(struct msg), IPC_NOWAIT) == -1);
223#endif
224
225	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
226}
227
228ATF_TC_CLEANUP(msgsnd_err, tc)
229{
230	clean();
231}
232
233ATF_TC_WITH_CLEANUP(msgsnd_nonblock);
234ATF_TC_HEAD(msgsnd_nonblock, tc)
235{
236	atf_tc_set_md_var(tc, "descr", "Test msgsnd(2) with IPC_NOWAIT");
237	atf_tc_set_md_var(tc, "timeout", "10");
238}
239
240ATF_TC_BODY(msgsnd_nonblock, tc)
241{
242	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
243	int id, rv, sta;
244	pid_t pid;
245
246	id = msgget(MSG_KEY, IPC_CREAT | 0600);
247	ATF_REQUIRE(id != -1);
248
249	pid = fork();
250	ATF_REQUIRE(pid >= 0);
251
252	if (pid == 0) {
253
254		for (;;) {
255
256			errno = 0;
257#ifdef	__FreeBSD__
258			rv = msgsnd(id, &msg, sizeof(msg.buf), IPC_NOWAIT);
259#else
260			rv = msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
261#endif
262
263			if (rv == -1 && errno == EAGAIN)
264				_exit(EXIT_SUCCESS);
265		}
266	}
267
268	(void)sleep(2);
269	(void)kill(pid, SIGKILL);
270	(void)wait(&sta);
271
272	if (WIFEXITED(sta) == 0 || WIFSIGNALED(sta) != 0)
273		atf_tc_fail("msgsnd(2) blocked with IPC_NOWAIT");
274
275	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
276}
277
278ATF_TC_CLEANUP(msgsnd_nonblock, tc)
279{
280	clean();
281}
282
283ATF_TC_WITH_CLEANUP(msgsnd_perm);
284ATF_TC_HEAD(msgsnd_perm, tc)
285{
286	atf_tc_set_md_var(tc, "descr", "Test permissions with msgsnd(2)");
287	atf_tc_set_md_var(tc, "require.user", "root");
288}
289
290ATF_TC_BODY(msgsnd_perm, tc)
291{
292	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
293	struct passwd *pw;
294	int id, sta;
295	pid_t pid;
296	uid_t uid;
297
298	pw = getpwnam("nobody");
299	id = msgget(MSG_KEY, IPC_CREAT | 0600);
300
301	ATF_REQUIRE(id != -1);
302	ATF_REQUIRE(pw != NULL);
303
304	uid = pw->pw_uid;
305	ATF_REQUIRE(uid != 0);
306
307	pid = fork();
308	ATF_REQUIRE(pid >= 0);
309
310	if (pid == 0) {
311
312		/*
313		 * Try to enqueue a message to the queue
314		 * created by root as RW for owner only.
315		 */
316		if (setuid(uid) != 0)
317			_exit(EX_OSERR);
318
319		id = msgget(MSG_KEY, 0);
320
321		if (id == -1)
322			_exit(EX_OSERR);
323
324		errno = 0;
325
326#ifdef	__FreeBSD__
327		if (msgsnd(id, &msg, sizeof(msg.buf), IPC_NOWAIT) == 0)
328#else
329		if (msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT) == 0)
330#endif
331			_exit(EXIT_FAILURE);
332
333		if (errno != EACCES)
334			_exit(EXIT_FAILURE);
335
336		_exit(EXIT_SUCCESS);
337	}
338
339	(void)wait(&sta);
340
341	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) {
342
343		if (errno == EX_OSERR)
344			atf_tc_fail("system call failed");
345
346		atf_tc_fail("UID %u enqueued message to root's queue", uid);
347	}
348
349	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
350}
351
352ATF_TC_CLEANUP(msgsnd_perm, tc)
353{
354	clean();
355}
356
357ATF_TP_ADD_TCS(tp)
358{
359
360	ATF_TP_ADD_TC(tp, msgsnd_block);
361	ATF_TP_ADD_TC(tp, msgsnd_count);
362	ATF_TP_ADD_TC(tp, msgsnd_err);
363	ATF_TP_ADD_TC(tp, msgsnd_nonblock);
364	ATF_TP_ADD_TC(tp, msgsnd_perm);
365
366	return atf_no_error();
367}
368