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