1/* $NetBSD: t_msgctl.c,v 1.7 2017/10/07 17:15:44 kre 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_msgctl.c,v 1.7 2017/10/07 17:15:44 kre 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		12345689
52#define MSG_MTYPE_1	0x41
53
54struct msg {
55	long		 mtype;
56	char		 buf[3];
57};
58
59static void		clean(void);
60
61static void
62clean(void)
63{
64	int id;
65
66	if ((id = msgget(MSG_KEY, 0)) != -1)
67		(void)msgctl(id, IPC_RMID, 0);
68}
69
70ATF_TC_WITH_CLEANUP(msgctl_err);
71ATF_TC_HEAD(msgctl_err, tc)
72{
73	atf_tc_set_md_var(tc, "descr", "Test errors from msgctl(2)");
74}
75
76ATF_TC_BODY(msgctl_err, tc)
77{
78	const int cmd[] = { IPC_STAT, IPC_SET, IPC_RMID };
79	struct msqid_ds msgds;
80	size_t i;
81	int id;
82
83	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
84
85	id = msgget(MSG_KEY, IPC_CREAT | 0600);
86	ATF_REQUIRE(id != -1);
87
88	errno = 0;
89	ATF_REQUIRE_ERRNO(EINVAL, msgctl(id, INT_MAX, &msgds) == -1);
90
91	errno = 0;
92	ATF_REQUIRE_ERRNO(EFAULT, msgctl(id, IPC_STAT, (void *)-1) == -1);
93
94	for (i = 0; i < __arraycount(cmd); i++) {
95		errno = 0;
96		ATF_REQUIRE_ERRNO(EINVAL, msgctl(-1, cmd[i], &msgds) == -1);
97	}
98
99	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
100}
101
102ATF_TC_CLEANUP(msgctl_err, tc)
103{
104	clean();
105}
106
107ATF_TC_WITH_CLEANUP(msgctl_perm);
108ATF_TC_HEAD(msgctl_perm, tc)
109{
110	atf_tc_set_md_var(tc, "descr", "Test permissions with msgctl(2)");
111	atf_tc_set_md_var(tc, "require.user", "root");
112}
113
114ATF_TC_BODY(msgctl_perm, tc)
115{
116	struct msqid_ds msgds;
117	struct passwd *pw;
118	pid_t pid;
119	int sta;
120	int id;
121
122	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
123
124	pw = getpwnam("nobody");
125	id = msgget(MSG_KEY, IPC_CREAT | 0600);
126
127	ATF_REQUIRE(id != -1);
128	ATF_REQUIRE(pw != NULL);
129	ATF_REQUIRE(msgctl(id, IPC_STAT, &msgds) == 0);
130
131	pid = fork();
132	ATF_REQUIRE(pid >= 0);
133
134	if (pid == 0) {
135
136		if (setuid(pw->pw_uid) != 0)
137			_exit(EX_OSERR);
138
139		msgds.msg_perm.uid = getuid();
140		msgds.msg_perm.gid = getgid();
141
142		errno = 0;
143
144		if (msgctl(id, IPC_SET, &msgds) == 0)
145			_exit(EXIT_FAILURE);
146
147		if (errno != EPERM)
148			_exit(EXIT_FAILURE);
149
150		(void)memset(&msgds, 0, sizeof(struct msqid_ds));
151
152		if (msgctl(id, IPC_STAT, &msgds) != 0)
153			_exit(EX_OSERR);
154
155		msgds.msg_qbytes = 1;
156
157		if (msgctl(id, IPC_SET, &msgds) == 0)
158			_exit(EXIT_FAILURE);
159
160		if (errno != EPERM)
161			_exit(EXIT_FAILURE);
162
163		_exit(EXIT_SUCCESS);
164	}
165
166	(void)wait(&sta);
167
168	if (WIFEXITED(sta) == 0) {
169
170		if (WEXITSTATUS(sta) == EX_OSERR)
171			atf_tc_fail("system call failed");
172
173		if (WEXITSTATUS(sta) == EXIT_FAILURE)
174			atf_tc_fail("UID %u manipulated root's "
175			    "message queue", pw->pw_uid);
176	}
177
178	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
179}
180
181ATF_TC_CLEANUP(msgctl_perm, tc)
182{
183	clean();
184}
185
186ATF_TC_WITH_CLEANUP(msgctl_pid);
187ATF_TC_HEAD(msgctl_pid, tc)
188{
189	atf_tc_set_md_var(tc, "descr", "Test that PIDs are updated");
190}
191
192ATF_TC_BODY(msgctl_pid, tc)
193{
194	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
195	struct msqid_ds msgds;
196	int id, sta;
197	pid_t pid;
198
199	id = msgget(MSG_KEY, IPC_CREAT | 0600);
200	ATF_REQUIRE(id != -1);
201
202	pid = fork();
203	ATF_REQUIRE(pid >= 0);
204
205	if (pid == 0) {
206
207		(void)msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
208
209		_exit(EXIT_SUCCESS);
210	}
211
212	(void)sleep(1);
213	(void)wait(&sta);
214	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
215
216	ATF_REQUIRE(msgctl(id, IPC_STAT, &msgds) == 0);
217
218	if (pid != msgds.msg_lspid)
219		atf_tc_fail("the PID of last msgsnd(2) was not updated");
220
221	pid = fork();
222	ATF_REQUIRE(pid >= 0);
223
224	if (pid == 0) {
225
226		(void)msgrcv(id, &msg,
227		    sizeof(struct msg), MSG_MTYPE_1, IPC_NOWAIT);
228
229		_exit(EXIT_SUCCESS);
230	}
231
232	(void)sleep(1);
233	(void)wait(&sta);
234	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
235
236	ATF_REQUIRE(msgctl(id, IPC_STAT, &msgds) == 0);
237
238	if (pid != msgds.msg_lrpid)
239		atf_tc_fail("the PID of last msgrcv(2) was not updated");
240
241	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
242}
243
244ATF_TC_CLEANUP(msgctl_pid, tc)
245{
246	clean();
247}
248
249ATF_TC_WITH_CLEANUP(msgctl_set);
250ATF_TC_HEAD(msgctl_set, tc)
251{
252	atf_tc_set_md_var(tc, "descr", "Test msgctl(2) with IPC_SET");
253	atf_tc_set_md_var(tc, "require.user", "root");
254}
255
256ATF_TC_BODY(msgctl_set, tc)
257{
258	struct msqid_ds msgds;
259	struct passwd *pw;
260	int id;
261
262	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
263
264	pw = getpwnam("nobody");
265	id = msgget(MSG_KEY, IPC_CREAT | 0600);
266
267	ATF_REQUIRE(id != -1);
268	ATF_REQUIRE(pw != NULL);
269	ATF_REQUIRE(msgctl(id, IPC_STAT, &msgds) == 0);
270
271	msgds.msg_perm.uid = pw->pw_uid;
272
273	if (msgctl(id, IPC_SET, &msgds) != 0)
274		atf_tc_fail("root failed to change the UID of message queue");
275
276	msgds.msg_perm.uid = getuid();
277	msgds.msg_perm.gid = pw->pw_gid;
278
279	if (msgctl(id, IPC_SET, &msgds) != 0)
280		atf_tc_fail("root failed to change the GID of message queue");
281
282	/*
283	 * Note: setting the qbytes to zero fails even as root.
284	 */
285	msgds.msg_qbytes = 1;
286	msgds.msg_perm.gid = getgid();
287
288	if (msgctl(id, IPC_SET, &msgds) != 0)
289		atf_tc_fail("root failed to change qbytes of message queue");
290
291	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
292}
293
294ATF_TC_CLEANUP(msgctl_set, tc)
295{
296	clean();
297}
298
299ATF_TC_WITH_CLEANUP(msgctl_time);
300ATF_TC_HEAD(msgctl_time, tc)
301{
302	atf_tc_set_md_var(tc, "descr", "Test that access times are updated");
303}
304
305ATF_TC_BODY(msgctl_time, tc)
306{
307	struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
308	struct msqid_ds msgds;
309	time_t t;
310	int id;
311
312	id = msgget(MSG_KEY, IPC_CREAT | 0600);
313	ATF_REQUIRE(id != -1);
314
315	t = time(NULL);
316
317	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
318	(void)msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
319	(void)msgctl(id, IPC_STAT, &msgds);
320
321	if (llabs(t - msgds.msg_stime) > 1)
322		atf_tc_fail("time of last msgsnd(2) was not updated");
323
324	if (msgds.msg_rtime != 0)
325		atf_tc_fail("time of last msgrcv(2) was updated incorrectly");
326
327	t = time(NULL);
328
329	(void)memset(&msgds, 0, sizeof(struct msqid_ds));
330	(void)msgrcv(id, &msg, sizeof(struct msg), MSG_MTYPE_1, IPC_NOWAIT);
331	(void)msgctl(id, IPC_STAT, &msgds);
332
333	if (llabs(t - msgds.msg_rtime) > 1)
334		atf_tc_fail("time of last msgrcv(2) was not updated");
335
336	/*
337	 * Note: this is non-zero even after the memset(3).
338	 */
339	if (msgds.msg_stime == 0)
340		atf_tc_fail("time of last msgsnd(2) was updated incorrectly");
341
342	ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
343}
344
345ATF_TC_CLEANUP(msgctl_time, tc)
346{
347	clean();
348}
349
350static volatile int sig_caught;
351
352static void
353sigsys_handler(int signum)
354{
355
356	sig_caught = signum;
357}
358
359static int
360no_kernel_sysvmsg(void)
361{
362	int id;
363	void (*osig)(int);
364
365	sig_caught = 0;
366	osig = signal(SIGSYS, sigsys_handler);
367	id = msgget(MSG_KEY, IPC_CREAT | 0600);
368	if (sig_caught || id == -1)
369		return 1;
370
371	(void)msgctl(id, IPC_RMID, 0);
372	(void)signal(SIGSYS, osig);
373
374	return 0;
375}
376
377ATF_TC(msgctl_query);
378ATF_TC_HEAD(msgctl_query, tc)
379{
380	atf_tc_set_md_var(tc, "descr", "Skip msgctl_* tests - no SYSVMSG");
381}
382ATF_TC_BODY(msgctl_query, tc)
383{
384	atf_tc_skip("No SYSVMSG in kernel");
385}
386
387ATF_TP_ADD_TCS(tp)
388{
389
390	if (no_kernel_sysvmsg()) {
391		ATF_TP_ADD_TC(tp, msgctl_query);
392	} else {
393		ATF_TP_ADD_TC(tp, msgctl_err);
394		ATF_TP_ADD_TC(tp, msgctl_perm);
395		ATF_TP_ADD_TC(tp, msgctl_pid);
396		ATF_TP_ADD_TC(tp, msgctl_set);
397		ATF_TP_ADD_TC(tp, msgctl_time);
398	}
399
400	return atf_no_error();
401}
402