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