1/*	$OpenBSD: t_msgrcv.c,v 1.2 2021/12/13 16:56:48 deraadt Exp $	*/
2/* $NetBSD: t_msgrcv.c,v 1.5 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#define MSG_LEN		3
57
58struct msg {
59	long		 mtype;
60	char		 buf[MSG_LEN];
61};
62
63static void		clean(void);
64
65static void
66clean(void)
67{
68	int id;
69
70	if ((id = msgget(MSG_KEY, 0)) != -1)
71		(void)msgctl(id, IPC_RMID, 0);
72}
73
74ATF_TC_WITH_CLEANUP(msgrcv_basic);
75ATF_TC_HEAD(msgrcv_basic, tc)
76{
77	atf_tc_set_md_var(tc, "descr", "A basic test of msgrcv(2)");
78}
79
80ATF_TC_BODY(msgrcv_basic, tc)
81{
82	struct msg msg1 = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
83	struct msg msg2 = { MSG_MTYPE_1, { 'x', 'y', 'z' } };
84	int id;
85
86	id = msgget(MSG_KEY, IPC_CREAT | 0600);
87	ATF_REQUIRE(id != -1);
88
89	(void)msgsnd(id, &msg1, MSG_LEN, IPC_NOWAIT);
90	(void)msgrcv(id, &msg2, MSG_LEN, MSG_MTYPE_1, IPC_NOWAIT);
91
92	ATF_CHECK(msg1.buf[0] == msg2.buf[0]);
93	ATF_CHECK(msg1.buf[1] == msg2.buf[1]);
94	ATF_CHECK(msg1.buf[2] == msg2.buf[2]);
95
96	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
97}
98
99ATF_TC_CLEANUP(msgrcv_basic, tc)
100{
101	clean();
102}
103
104ATF_TC_WITH_CLEANUP(msgrcv_block);
105ATF_TC_HEAD(msgrcv_block, tc)
106{
107	atf_tc_set_md_var(tc, "descr", "Test that msgrcv(2) blocks");
108}
109
110ATF_TC_BODY(msgrcv_block, tc)
111{
112	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
113	int id, sta;
114	pid_t pid;
115
116	id = msgget(MSG_KEY, IPC_CREAT | 0600);
117	ATF_REQUIRE(id != -1);
118
119	pid = fork();
120	ATF_REQUIRE(pid >= 0);
121
122	if (pid == 0) {
123
124		if (msgrcv(id, &msg, MSG_LEN, MSG_MTYPE_1, 0) < 0)
125			_exit(EXIT_FAILURE);
126
127		_exit(EXIT_SUCCESS);
128	}
129
130	/*
131	 * Below msgsnd(2) should unblock the child,
132	 * and hence kill(2) should fail with ESRCH.
133	 */
134	(void)sleep(1);
135	(void)msgsnd(id, &msg, MSG_LEN, IPC_NOWAIT);
136	(void)sleep(1);
137	(void)kill(pid, SIGKILL);
138	(void)wait(&sta);
139
140	if (WIFEXITED(sta) == 0 || WIFSIGNALED(sta) != 0)
141		atf_tc_fail("msgrcv(2) did not block");
142
143	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
144}
145
146ATF_TC_CLEANUP(msgrcv_block, tc)
147{
148	clean();
149}
150
151ATF_TC_WITH_CLEANUP(msgrcv_err);
152ATF_TC_HEAD(msgrcv_err, tc)
153{
154	atf_tc_set_md_var(tc, "descr", "Test errors from msgrcv(2)");
155}
156
157ATF_TC_BODY(msgrcv_err, tc)
158{
159	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
160	int id, r = 0;
161
162	id = msgget(MSG_KEY, IPC_CREAT | 0600);
163	ATF_REQUIRE(id != -1);
164
165	errno = 0;
166
167	ATF_REQUIRE_ERRNO(ENOMSG, msgrcv(id, &msg,
168		MSG_LEN, MSG_MTYPE_1, IPC_NOWAIT) == -1);
169
170	ATF_REQUIRE(msgsnd(id, &msg, MSG_LEN, IPC_NOWAIT) == 0);
171
172	errno = 0;
173
174	ATF_REQUIRE_ERRNO(EFAULT, msgrcv(id, (void *)-1,
175		MSG_LEN, MSG_MTYPE_1, IPC_NOWAIT) == -1);
176
177	errno = 0;
178
179	ATF_REQUIRE_ERRNO(EINVAL, msgrcv(-1, &msg,
180		MSG_LEN, MSG_MTYPE_1, IPC_NOWAIT) == -1);
181
182	errno = 0;
183
184	ATF_REQUIRE_ERRNO(EINVAL, msgrcv(-1, &msg,
185		SSIZE_MAX, MSG_MTYPE_1, IPC_NOWAIT) == -1);
186
187	ATF_REQUIRE(msgsnd(id, &msg, MSG_LEN, IPC_NOWAIT) == 0);
188
189	errno = 0;
190
191	ATF_REQUIRE_ERRNO(E2BIG, msgrcv(id, &r,
192		MSG_LEN - 1, MSG_MTYPE_1, IPC_NOWAIT) == -1);
193
194	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
195}
196
197ATF_TC_CLEANUP(msgrcv_err, tc)
198{
199	clean();
200}
201
202
203ATF_TC_WITH_CLEANUP(msgrcv_mtype);
204ATF_TC_HEAD(msgrcv_mtype, tc)
205{
206	atf_tc_set_md_var(tc, "descr", "Test message types with msgrcv(2)");
207}
208
209ATF_TC_BODY(msgrcv_mtype, tc)
210{
211	struct msg msg1 = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
212	struct msg msg2 = { MSG_MTYPE_3, { 'x', 'y', 'z' } };
213	int id;
214
215	id = msgget(MSG_KEY, IPC_CREAT | 0600);
216	ATF_REQUIRE(id != -1);
217
218	(void)msgsnd(id, &msg1, MSG_LEN, IPC_NOWAIT);
219	(void)msgrcv(id, &msg2, MSG_LEN, MSG_MTYPE_2, IPC_NOWAIT);
220
221	ATF_CHECK(msg1.buf[0] != msg2.buf[0]);	/* Different mtype. */
222	ATF_CHECK(msg1.buf[1] != msg2.buf[1]);
223	ATF_CHECK(msg1.buf[2] != msg2.buf[2]);
224
225	(void)msgrcv(id, &msg2, MSG_LEN, MSG_MTYPE_1, IPC_NOWAIT);
226
227	ATF_CHECK(msg1.buf[0] == msg2.buf[0]);	/* Same mtype. */
228	ATF_CHECK(msg1.buf[1] == msg2.buf[1]);
229	ATF_CHECK(msg1.buf[2] == msg2.buf[2]);
230
231	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
232}
233
234ATF_TC_CLEANUP(msgrcv_mtype, tc)
235{
236	clean();
237}
238
239ATF_TC_WITH_CLEANUP(msgrcv_nonblock);
240ATF_TC_HEAD(msgrcv_nonblock, tc)
241{
242	atf_tc_set_md_var(tc, "descr", "Test msgrcv(2) with IPC_NOWAIT");
243	atf_tc_set_md_var(tc, "timeout", "10");
244}
245
246ATF_TC_BODY(msgrcv_nonblock, tc)
247{
248	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
249	const ssize_t n = 10;
250	int id, sta;
251	ssize_t i;
252	pid_t pid;
253
254	id = msgget(MSG_KEY, IPC_CREAT | 0600);
255	ATF_REQUIRE(id != -1);
256
257	for (i = 0; i < n; i++) {
258
259		ATF_REQUIRE(msgsnd(id, &msg, MSG_LEN, IPC_NOWAIT) == 0);
260	}
261
262	pid = fork();
263	ATF_REQUIRE(pid >= 0);
264
265	if (pid == 0) {
266
267		while (i != 0) {
268
269			if (msgrcv(id, &msg, MSG_LEN, MSG_MTYPE_1,
270			    IPC_NOWAIT) == -1)
271				_exit(EXIT_FAILURE);
272
273			i--;
274		}
275
276		_exit(EXIT_SUCCESS);
277	}
278
279	(void)sleep(2);
280	(void)kill(pid, SIGKILL);
281	(void)wait(&sta);
282
283	if (WIFSIGNALED(sta) != 0 || WTERMSIG(sta) == SIGKILL)
284		atf_tc_fail("msgrcv(2) blocked with IPC_NOWAIT");
285
286	if (WIFEXITED(sta) == 0 && WEXITSTATUS(sta) != EXIT_SUCCESS)
287		atf_tc_fail("msgrcv(2) failed");
288
289	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
290}
291
292ATF_TC_CLEANUP(msgrcv_nonblock, tc)
293{
294	clean();
295}
296
297ATF_TC_WITH_CLEANUP(msgrcv_truncate);
298ATF_TC_HEAD(msgrcv_truncate, tc)
299{
300	atf_tc_set_md_var(tc, "descr", "Test msgrcv(2) with MSG_NOERROR");
301}
302
303ATF_TC_BODY(msgrcv_truncate, tc)
304{
305#define	MSG_SMALLLEN	2
306	struct msgsmall {
307		long		 mtype;
308		char		 buf[MSG_SMALLLEN];
309	};
310
311	struct msg msg1 = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
312	struct msgsmall msg2 = { MSG_MTYPE_1, { 'x', 'y' } };
313	int id;
314
315	id = msgget(MSG_KEY, IPC_CREAT | 0600);
316	ATF_REQUIRE(id != -1);
317
318	(void)msgsnd(id, &msg1, MSG_LEN, IPC_NOWAIT);
319	(void)msgrcv(id, &msg2, MSG_SMALLLEN,
320	    MSG_MTYPE_1, IPC_NOWAIT | MSG_NOERROR);
321
322	ATF_CHECK(msg1.buf[0] == msg2.buf[0]);
323	ATF_CHECK(msg1.buf[1] == msg2.buf[1]);
324
325	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
326}
327
328ATF_TC_CLEANUP(msgrcv_truncate, tc)
329{
330	clean();
331}
332
333static volatile int sig_caught;
334
335static void
336sigsys_handler(int signum)
337{
338
339	sig_caught = signum;
340}
341
342static int
343no_kernel_sysvmsg(void)
344{
345	int id;
346	void (*osig)(int);
347
348	sig_caught = 0;
349	osig = signal(SIGSYS, sigsys_handler);
350	id = msgget(MSG_KEY, IPC_CREAT | 0600);
351	if (sig_caught || id == -1)
352		return 1;
353
354	(void)msgctl(id, IPC_RMID, 0);
355	(void)signal(SIGSYS, osig);
356
357	return 0;
358}
359
360ATF_TC(msgrcv_query);
361ATF_TC_HEAD(msgrcv_query, tc)
362{
363	atf_tc_set_md_var(tc, "descr", "Skip msgrcv_* tests - no SYSVMSG");
364}
365ATF_TC_BODY(msgrcv_query, tc)
366{
367	atf_tc_skip("No SYSVMSG in kernel");
368}
369
370ATF_TP_ADD_TCS(tp)
371{
372
373	if (no_kernel_sysvmsg()) {
374		ATF_TP_ADD_TC(tp, msgrcv_query);
375	} else {
376		ATF_TP_ADD_TC(tp, msgrcv_basic);
377		ATF_TP_ADD_TC(tp, msgrcv_block);
378		ATF_TP_ADD_TC(tp, msgrcv_err);
379		ATF_TP_ADD_TC(tp, msgrcv_mtype);
380		ATF_TP_ADD_TC(tp, msgrcv_nonblock);
381		ATF_TP_ADD_TC(tp, msgrcv_truncate);
382	}
383
384	return atf_no_error();
385}
386