xref: /netbsd-src/tests/lib/libc/sys/t_msgctl.c (revision 6618a9bf1f38af5e586d846dc683ef0f5ec8e713)
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 
54 struct msg {
55 	long		 mtype;
56 	char		 buf[3];
57 };
58 
59 static void		clean(void);
60 
61 static void
clean(void)62 clean(void)
63 {
64 	int id;
65 
66 	if ((id = msgget(MSG_KEY, 0)) != -1)
67 		(void)msgctl(id, IPC_RMID, 0);
68 }
69 
70 ATF_TC_WITH_CLEANUP(msgctl_err);
ATF_TC_HEAD(msgctl_err,tc)71 ATF_TC_HEAD(msgctl_err, tc)
72 {
73 	atf_tc_set_md_var(tc, "descr", "Test errors from msgctl(2)");
74 }
75 
ATF_TC_BODY(msgctl_err,tc)76 ATF_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 
ATF_TC_CLEANUP(msgctl_err,tc)102 ATF_TC_CLEANUP(msgctl_err, tc)
103 {
104 	clean();
105 }
106 
107 ATF_TC_WITH_CLEANUP(msgctl_perm);
ATF_TC_HEAD(msgctl_perm,tc)108 ATF_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 
ATF_TC_BODY(msgctl_perm,tc)114 ATF_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 
ATF_TC_CLEANUP(msgctl_perm,tc)181 ATF_TC_CLEANUP(msgctl_perm, tc)
182 {
183 	clean();
184 }
185 
186 ATF_TC_WITH_CLEANUP(msgctl_pid);
ATF_TC_HEAD(msgctl_pid,tc)187 ATF_TC_HEAD(msgctl_pid, tc)
188 {
189 	atf_tc_set_md_var(tc, "descr", "Test that PIDs are updated");
190 }
191 
ATF_TC_BODY(msgctl_pid,tc)192 ATF_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 
ATF_TC_CLEANUP(msgctl_pid,tc)244 ATF_TC_CLEANUP(msgctl_pid, tc)
245 {
246 	clean();
247 }
248 
249 ATF_TC_WITH_CLEANUP(msgctl_set);
ATF_TC_HEAD(msgctl_set,tc)250 ATF_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 
ATF_TC_BODY(msgctl_set,tc)256 ATF_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 
ATF_TC_CLEANUP(msgctl_set,tc)294 ATF_TC_CLEANUP(msgctl_set, tc)
295 {
296 	clean();
297 }
298 
299 ATF_TC_WITH_CLEANUP(msgctl_time);
ATF_TC_HEAD(msgctl_time,tc)300 ATF_TC_HEAD(msgctl_time, tc)
301 {
302 	atf_tc_set_md_var(tc, "descr", "Test that access times are updated");
303 }
304 
ATF_TC_BODY(msgctl_time,tc)305 ATF_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 
ATF_TC_CLEANUP(msgctl_time,tc)345 ATF_TC_CLEANUP(msgctl_time, tc)
346 {
347 	clean();
348 }
349 
350 static volatile int sig_caught;
351 
352 static void
sigsys_handler(int signum)353 sigsys_handler(int signum)
354 {
355 
356 	sig_caught = signum;
357 }
358 
359 static int
no_kernel_sysvmsg(void)360 no_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 
377 ATF_TC(msgctl_query);
ATF_TC_HEAD(msgctl_query,tc)378 ATF_TC_HEAD(msgctl_query, tc)
379 {
380 	atf_tc_set_md_var(tc, "descr", "Skip msgctl_* tests - no SYSVMSG");
381 }
ATF_TC_BODY(msgctl_query,tc)382 ATF_TC_BODY(msgctl_query, tc)
383 {
384 	atf_tc_skip("No SYSVMSG in kernel");
385 }
386 
ATF_TP_ADD_TCS(tp)387 ATF_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