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 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); 72 ATF_TC_HEAD(msgctl_err, tc) 73 { 74 atf_tc_set_md_var(tc, "descr", "Test errors from msgctl(2)"); 75 } 76 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 103 ATF_TC_CLEANUP(msgctl_err, tc) 104 { 105 clean(); 106 } 107 108 ATF_TC_WITH_CLEANUP(msgctl_perm); 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 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 182 ATF_TC_CLEANUP(msgctl_perm, tc) 183 { 184 clean(); 185 } 186 187 ATF_TC_WITH_CLEANUP(msgctl_pid); 188 ATF_TC_HEAD(msgctl_pid, tc) 189 { 190 atf_tc_set_md_var(tc, "descr", "Test that PIDs are updated"); 191 } 192 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 245 ATF_TC_CLEANUP(msgctl_pid, tc) 246 { 247 clean(); 248 } 249 250 ATF_TC_WITH_CLEANUP(msgctl_set); 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 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 295 ATF_TC_CLEANUP(msgctl_set, tc) 296 { 297 clean(); 298 } 299 300 ATF_TC_WITH_CLEANUP(msgctl_time); 301 ATF_TC_HEAD(msgctl_time, tc) 302 { 303 atf_tc_set_md_var(tc, "descr", "Test that access times are updated"); 304 } 305 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 346 ATF_TC_CLEANUP(msgctl_time, tc) 347 { 348 clean(); 349 } 350 351 static volatile int sig_caught; 352 353 static void 354 sigsys_handler(int signum) 355 { 356 357 sig_caught = signum; 358 } 359 360 static int 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); 379 ATF_TC_HEAD(msgctl_query, tc) 380 { 381 atf_tc_set_md_var(tc, "descr", "Skip msgctl_* tests - no SYSVMSG"); 382 } 383 ATF_TC_BODY(msgctl_query, tc) 384 { 385 atf_tc_skip("No SYSVMSG in kernel"); 386 } 387 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