1 /* $OpenBSD: t_msgsnd.c,v 1.2 2021/12/13 16:56:48 deraadt Exp $ */
2 /* $NetBSD: t_msgsnd.c,v 1.4 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
57 struct msg {
58 long mtype;
59 char buf[3];
60 };
61
62 static void clean(void);
63
64 static void
clean(void)65 clean(void)
66 {
67 int id;
68
69 if ((id = msgget(MSG_KEY, 0)) != -1)
70 (void)msgctl(id, IPC_RMID, 0);
71 }
72
73 ATF_TC_WITH_CLEANUP(msgsnd_block);
ATF_TC_HEAD(msgsnd_block,tc)74 ATF_TC_HEAD(msgsnd_block, tc)
75 {
76 atf_tc_set_md_var(tc, "descr", "Test that msgsnd(2) blocks");
77 atf_tc_set_md_var(tc, "timeout", "10");
78 }
79
ATF_TC_BODY(msgsnd_block,tc)80 ATF_TC_BODY(msgsnd_block, tc)
81 {
82 struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
83 int id, sta;
84 pid_t pid;
85
86 id = msgget(MSG_KEY, IPC_CREAT | 0600);
87 ATF_REQUIRE(id != -1);
88
89 pid = fork();
90 ATF_REQUIRE(pid >= 0);
91
92 if (pid == 0) {
93
94 /*
95 * Enqueue messages until some limit (e.g. the maximum
96 * number of messages in the queue or the maximum number
97 * of bytes in the queue) is reached. After this the call
98 * should block when the IPC_NOWAIT is not set.
99 */
100 for (;;) {
101
102 if (msgsnd(id, &msg, sizeof(struct msg), 0) < 0)
103 _exit(EXIT_FAILURE);
104 }
105 }
106
107 (void)sleep(2);
108 (void)kill(pid, SIGKILL);
109 (void)wait(&sta);
110
111 if (WIFEXITED(sta) != 0 || WIFSIGNALED(sta) == 0)
112 atf_tc_fail("msgsnd(2) did not block");
113
114 ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
115 }
116
ATF_TC_CLEANUP(msgsnd_block,tc)117 ATF_TC_CLEANUP(msgsnd_block, tc)
118 {
119 clean();
120 }
121
122 ATF_TC_WITH_CLEANUP(msgsnd_count);
ATF_TC_HEAD(msgsnd_count,tc)123 ATF_TC_HEAD(msgsnd_count, tc)
124 {
125 atf_tc_set_md_var(tc, "descr",
126 "Test that msgsnd(2) increments the amount of "
127 "message in the queue, as given by msgctl(2)");
128 atf_tc_set_md_var(tc, "timeout", "10");
129 }
130
ATF_TC_BODY(msgsnd_count,tc)131 ATF_TC_BODY(msgsnd_count, tc)
132 {
133 struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
134 struct msqid_ds ds;
135 size_t i = 0;
136 int id, rv;
137
138 id = msgget(MSG_KEY, IPC_CREAT | 0600);
139 ATF_REQUIRE(id != -1);
140
141 for (;;) {
142
143 errno = 0;
144 rv = msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
145
146 if (rv == 0) {
147 i++;
148 continue;
149 }
150
151 if (rv == -1 && errno == EAGAIN)
152 break;
153
154 atf_tc_fail("failed to enqueue a message");
155 }
156
157 (void)memset(&ds, 0, sizeof(struct msqid_ds));
158 (void)msgctl(id, IPC_STAT, &ds);
159
160 if (ds.msg_qnum != i)
161 atf_tc_fail("incorrect message count");
162
163 ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
164 }
165
ATF_TC_CLEANUP(msgsnd_count,tc)166 ATF_TC_CLEANUP(msgsnd_count, tc)
167 {
168 clean();
169 }
170
171 ATF_TC_WITH_CLEANUP(msgsnd_err);
ATF_TC_HEAD(msgsnd_err,tc)172 ATF_TC_HEAD(msgsnd_err, tc)
173 {
174 atf_tc_set_md_var(tc, "descr", "Test errors from msgsnd(2)");
175 }
176
ATF_TC_BODY(msgsnd_err,tc)177 ATF_TC_BODY(msgsnd_err, tc)
178 {
179 struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
180 int id;
181
182 id = msgget(MSG_KEY, IPC_CREAT | 0600);
183 ATF_REQUIRE(id != -1);
184
185 errno = 0;
186
187 ATF_REQUIRE_ERRNO(EFAULT, msgsnd(id, (void *)-1,
188 sizeof(struct msg), IPC_NOWAIT) == -1);
189
190 errno = 0;
191
192 ATF_REQUIRE_ERRNO(EINVAL, msgsnd(-1, &msg,
193 sizeof(struct msg), IPC_NOWAIT) == -1);
194
195 errno = 0;
196
197 ATF_REQUIRE_ERRNO(EINVAL, msgsnd(-1, &msg,
198 SSIZE_MAX, IPC_NOWAIT) == -1);
199
200 errno = 0;
201 msg.mtype = 0;
202
203 ATF_REQUIRE_ERRNO(EINVAL, msgsnd(id, &msg,
204 sizeof(struct msg), IPC_NOWAIT) == -1);
205
206 ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
207 }
208
ATF_TC_CLEANUP(msgsnd_err,tc)209 ATF_TC_CLEANUP(msgsnd_err, tc)
210 {
211 clean();
212 }
213
214 ATF_TC_WITH_CLEANUP(msgsnd_nonblock);
ATF_TC_HEAD(msgsnd_nonblock,tc)215 ATF_TC_HEAD(msgsnd_nonblock, tc)
216 {
217 atf_tc_set_md_var(tc, "descr", "Test msgsnd(2) with IPC_NOWAIT");
218 atf_tc_set_md_var(tc, "timeout", "10");
219 }
220
ATF_TC_BODY(msgsnd_nonblock,tc)221 ATF_TC_BODY(msgsnd_nonblock, tc)
222 {
223 struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
224 int id, rv, sta;
225 pid_t pid;
226
227 id = msgget(MSG_KEY, IPC_CREAT | 0600);
228 ATF_REQUIRE(id != -1);
229
230 pid = fork();
231 ATF_REQUIRE(pid >= 0);
232
233 if (pid == 0) {
234
235 for (;;) {
236
237 errno = 0;
238 rv = msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT);
239
240 if (rv == -1 && errno == EAGAIN)
241 _exit(EXIT_SUCCESS);
242 }
243 }
244
245 (void)sleep(2);
246 (void)kill(pid, SIGKILL);
247 (void)wait(&sta);
248
249 if (WIFEXITED(sta) == 0 || WIFSIGNALED(sta) != 0)
250 atf_tc_fail("msgsnd(2) blocked with IPC_NOWAIT");
251
252 ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
253 }
254
ATF_TC_CLEANUP(msgsnd_nonblock,tc)255 ATF_TC_CLEANUP(msgsnd_nonblock, tc)
256 {
257 clean();
258 }
259
260 ATF_TC_WITH_CLEANUP(msgsnd_perm);
ATF_TC_HEAD(msgsnd_perm,tc)261 ATF_TC_HEAD(msgsnd_perm, tc)
262 {
263 atf_tc_set_md_var(tc, "descr", "Test permissions with msgsnd(2)");
264 atf_tc_set_md_var(tc, "require.user", "root");
265 }
266
ATF_TC_BODY(msgsnd_perm,tc)267 ATF_TC_BODY(msgsnd_perm, tc)
268 {
269 struct msg msg = { MSG_MTYPE_1, { 'a', 'b', 'c' } };
270 struct passwd *pw;
271 int id, sta;
272 pid_t pid;
273 uid_t uid;
274
275 pw = getpwnam("nobody");
276 id = msgget(MSG_KEY, IPC_CREAT | 0600);
277
278 ATF_REQUIRE(id != -1);
279 ATF_REQUIRE(pw != NULL);
280
281 uid = pw->pw_uid;
282 ATF_REQUIRE(uid != 0);
283
284 pid = fork();
285 ATF_REQUIRE(pid >= 0);
286
287 if (pid == 0) {
288
289 /*
290 * Try to enqueue a message to the queue
291 * created by root as RW for owner only.
292 */
293 if (setuid(uid) != 0)
294 _exit(EX_OSERR);
295
296 id = msgget(MSG_KEY, 0);
297
298 if (id == -1)
299 _exit(EX_OSERR);
300
301 errno = 0;
302
303 if (msgsnd(id, &msg, sizeof(struct msg), IPC_NOWAIT) == 0)
304 _exit(EXIT_FAILURE);
305
306 if (errno != EACCES)
307 _exit(EXIT_FAILURE);
308
309 _exit(EXIT_SUCCESS);
310 }
311
312 (void)wait(&sta);
313
314 if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) {
315
316 if (errno == EX_OSERR)
317 atf_tc_fail("system call failed");
318
319 atf_tc_fail("UID %u enqueued message to root's queue", uid);
320 }
321
322 ATF_REQUIRE(msgctl(id, IPC_RMID, 0) == 0);
323 }
324
ATF_TC_CLEANUP(msgsnd_perm,tc)325 ATF_TC_CLEANUP(msgsnd_perm, tc)
326 {
327 clean();
328 }
329
330 static volatile int sig_caught;
331
332 static void
sigsys_handler(int signum)333 sigsys_handler(int signum)
334 {
335
336 sig_caught = signum;
337 }
338
339 static int
no_kernel_sysvmsg(void)340 no_kernel_sysvmsg(void)
341 {
342 int id;
343 void (*osig)(int);
344
345 sig_caught = 0;
346 osig = signal(SIGSYS, sigsys_handler);
347 id = msgget(MSG_KEY, IPC_CREAT | 0600);
348 if (sig_caught || id == -1)
349 return 1;
350
351 (void)msgctl(id, IPC_RMID, 0);
352 (void)signal(SIGSYS, osig);
353
354 return 0;
355 }
356
357 ATF_TC(msgsnd_query);
ATF_TC_HEAD(msgsnd_query,tc)358 ATF_TC_HEAD(msgsnd_query, tc)
359 {
360 atf_tc_set_md_var(tc, "descr", "Skip msgsnd_* tests - no SYSVMSG");
361 }
ATF_TC_BODY(msgsnd_query,tc)362 ATF_TC_BODY(msgsnd_query, tc)
363 {
364 atf_tc_skip("No SYSVMSG in kernel");
365 }
366
ATF_TP_ADD_TCS(tp)367 ATF_TP_ADD_TCS(tp)
368 {
369
370 if (no_kernel_sysvmsg()) {
371 ATF_TP_ADD_TC(tp, msgsnd_query);
372 } else {
373 ATF_TP_ADD_TC(tp, msgsnd_block);
374 ATF_TP_ADD_TC(tp, msgsnd_count);
375 ATF_TP_ADD_TC(tp, msgsnd_err);
376 ATF_TP_ADD_TC(tp, msgsnd_nonblock);
377 ATF_TP_ADD_TC(tp, msgsnd_perm);
378 }
379
380 return atf_no_error();
381 }
382