1/* $NetBSD: t_msgsnd.c,v 1.1 2011/11/05 07:45:41 jruoho 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.1 2011/11/05 07:45:41 jruoho 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 <pwd.h>
42#include <signal.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <sysexits.h>
47#include <time.h>
48#include <unistd.h>
49
50#define MSG_KEY		1234
51#define MSG_MTYPE_1	0x41
52#define	MSG_MTYPE_2	0x42
53#define MSG_MTYPE_3	0x43
54
55struct msg {
56	long		 mtype;
57	char		 buf[3];
58};
59
60static void		clean(void);
61
62static void
63clean(void)
64{
65	int id;
66
67	if ((id = msgget(MSG_KEY, 0)) != -1)
68		(void)msgctl(id, IPC_RMID, 0);
69}
70
71ATF_TC_WITH_CLEANUP(msgsnd_block);
72ATF_TC_HEAD(msgsnd_block, tc)
73{
74	atf_tc_set_md_var(tc, "descr", "Test that msgsnd(2) blocks");
75	atf_tc_set_md_var(tc, "timeout", "10");
76}
77
78ATF_TC_BODY(msgsnd_block, tc)
79{
80	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
81	int id, sta;
82	pid_t pid;
83
84	id = msgget(MSG_KEY, IPC_CREAT | 0600);
85	ATF_REQUIRE(id != -1);
86
87	pid = fork();
88	ATF_REQUIRE(pid >= 0);
89
90	if (pid == 0) {
91
92		/*
93		 * Enqueue messages until some limit (e.g. the maximum
94		 * number of messages in the queue or the maximum number
95		 * of bytes in the queue) is reached. After this the call
96		 * should block when the IPC_NOWAIT is not set.
97		 */
98		for (;;) {
99
100			if (msgsnd(id, &msg, sizeof(struct msg), 0) < 0)
101				_exit(EXIT_FAILURE);
102		}
103	}
104
105	(void)sleep(2);
106	(void)kill(pid, SIGKILL);
107	(void)wait(&sta);
108
109	if (WIFEXITED(sta) != 0 || WIFSIGNALED(sta) == 0)
110		atf_tc_fail("msgsnd(2) did not block");
111
112	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
113}
114
115ATF_TC_CLEANUP(msgsnd_block, tc)
116{
117	clean();
118}
119
120ATF_TC_WITH_CLEANUP(msgsnd_count);
121ATF_TC_HEAD(msgsnd_count, tc)
122{
123	atf_tc_set_md_var(tc, "descr",
124	    "Test that msgsnd(2) increments the amount of "
125	    "message in the queue, as given by msgctl(2)");
126	atf_tc_set_md_var(tc, "timeout", "10");
127}
128
129ATF_TC_BODY(msgsnd_count, tc)
130{
131	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
132	struct msqid_ds ds;
133	size_t i = 0;
134	int id, rv;
135
136	id = msgget(MSG_KEY, IPC_CREAT | 0600);
137	ATF_REQUIRE(id != -1);
138
139	for (;;) {
140
141		errno = 0;
142		rv = msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
143
144		if (rv == 0) {
145			i++;
146			continue;
147		}
148
149		if (rv == -1 && errno == EAGAIN)
150			break;
151
152		atf_tc_fail("failed to enqueue a message");
153	}
154
155	(void)memset(&ds, 0, sizeof(struct msqid_ds));
156	(void)msgctl(id, IPC_STAT, &ds);
157
158	if (ds.msg_qnum != i)
159		atf_tc_fail("incorrect message count");
160
161	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
162}
163
164ATF_TC_CLEANUP(msgsnd_count, tc)
165{
166	clean();
167}
168
169ATF_TC_WITH_CLEANUP(msgsnd_err);
170ATF_TC_HEAD(msgsnd_err, tc)
171{
172	atf_tc_set_md_var(tc, "descr", "Test errors from msgsnd(2)");
173}
174
175ATF_TC_BODY(msgsnd_err, tc)
176{
177	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
178	int id;
179
180	id = msgget(MSG_KEY, IPC_CREAT | 0600);
181	ATF_REQUIRE(id != -1);
182
183	errno = 0;
184
185	ATF_REQUIRE_ERRNO(EFAULT, msgsnd(id, (void *)-1,
186		sizeof(struct msg), IPC_NOWAIT) == -1);
187
188	errno = 0;
189
190	ATF_REQUIRE_ERRNO(EINVAL, msgsnd(-1, &msg,
191		sizeof(struct msg), IPC_NOWAIT) == -1);
192
193	errno = 0;
194
195	ATF_REQUIRE_ERRNO(EINVAL, msgsnd(-1, &msg,
196		SSIZE_MAX, IPC_NOWAIT) == -1);
197
198	errno = 0;
199	msg.mtype = 0;
200
201	ATF_REQUIRE_ERRNO(EINVAL, msgsnd(id, &msg,
202		sizeof(struct msg), IPC_NOWAIT) == -1);
203
204	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
205}
206
207ATF_TC_CLEANUP(msgsnd_err, tc)
208{
209	clean();
210}
211
212ATF_TC_WITH_CLEANUP(msgsnd_nonblock);
213ATF_TC_HEAD(msgsnd_nonblock, tc)
214{
215	atf_tc_set_md_var(tc, "descr", "Test msgsnd(2) with IPC_NOWAIT");
216	atf_tc_set_md_var(tc, "timeout", "10");
217}
218
219ATF_TC_BODY(msgsnd_nonblock, tc)
220{
221	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
222	int id, rv, sta;
223	pid_t pid;
224
225	id = msgget(MSG_KEY, IPC_CREAT | 0600);
226	ATF_REQUIRE(id != -1);
227
228	pid = fork();
229	ATF_REQUIRE(pid >= 0);
230
231	if (pid == 0) {
232
233		for (;;) {
234
235			errno = 0;
236			rv = msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
237
238			if (rv == -1 && errno == EAGAIN)
239				_exit(EXIT_SUCCESS);
240		}
241	}
242
243	(void)sleep(2);
244	(void)kill(pid, SIGKILL);
245	(void)wait(&sta);
246
247	if (WIFEXITED(sta) == 0 || WIFSIGNALED(sta) != 0)
248		atf_tc_fail("msgsnd(2) blocked with IPC_NOWAIT");
249
250	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
251}
252
253ATF_TC_CLEANUP(msgsnd_nonblock, tc)
254{
255	clean();
256}
257
258ATF_TC_WITH_CLEANUP(msgsnd_perm);
259ATF_TC_HEAD(msgsnd_perm, tc)
260{
261	atf_tc_set_md_var(tc, "descr", "Test permissions with msgsnd(2)");
262	atf_tc_set_md_var(tc, "require.user", "root");
263}
264
265ATF_TC_BODY(msgsnd_perm, tc)
266{
267	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
268	struct passwd *pw;
269	int id, sta;
270	pid_t pid;
271	uid_t uid;
272
273	pw = getpwnam("nobody");
274	id = msgget(MSG_KEY, IPC_CREAT | 0600);
275
276	ATF_REQUIRE(id != -1);
277	ATF_REQUIRE(pw != NULL);
278
279	uid = pw->pw_uid;
280	ATF_REQUIRE(uid != 0);
281
282	pid = fork();
283	ATF_REQUIRE(pid >= 0);
284
285	if (pid == 0) {
286
287		/*
288		 * Try to enqueue a message to the queue
289		 * created by root as RW for owner only.
290		 */
291		if (setuid(uid) != 0)
292			_exit(EX_OSERR);
293
294		id = msgget(MSG_KEY, 0);
295
296		if (id == -1)
297			_exit(EX_OSERR);
298
299		errno = 0;
300
301		if (msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT) == 0)
302			_exit(EXIT_FAILURE);
303
304		if (errno != EACCES)
305			_exit(EXIT_FAILURE);
306
307		_exit(EXIT_SUCCESS);
308	}
309
310	(void)wait(&sta);
311
312	if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) {
313
314		if (errno == EX_OSERR)
315			atf_tc_fail("system call failed");
316
317		atf_tc_fail("UID %u enqueued message to root's queue", uid);
318	}
319
320	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
321}
322
323ATF_TC_CLEANUP(msgsnd_perm, tc)
324{
325	clean();
326}
327
328ATF_TP_ADD_TCS(tp)
329{
330
331	ATF_TP_ADD_TC(tp, msgsnd_block);
332	ATF_TP_ADD_TC(tp, msgsnd_count);
333	ATF_TP_ADD_TC(tp, msgsnd_err);
334	ATF_TP_ADD_TC(tp, msgsnd_nonblock);
335	ATF_TP_ADD_TC(tp, msgsnd_perm);
336
337	return atf_no_error();
338}
339