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