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