1/* 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * Implementation of SVID messages 30 * 31 * Author: Daniel Boulet 32 * 33 * Copyright 1993 Daniel Boulet and RTMX Inc. 34 * 35 * This system call was implemented by Daniel Boulet under contract from RTMX. 36 * 37 * Redistribution and use in source forms, with and without modification, 38 * are permitted provided that this entire comment appears intact. 39 * 40 * Redistribution in binary form may occur without any restrictions. 41 * Obviously, it would be nice if you gave credit where credit is due 42 * but requiring it would be too onerous. 43 * 44 * This software is provided ``AS IS'' without any warranties of any kind. 45 */ 46/* 47 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 48 * support for mandatory and extensible security protections. This notice 49 * is included in support of clause 2.2 (b) of the Apple Public License, 50 * Version 2.0. 51 */ 52 53#include <sys/param.h> 54#include <sys/systm.h> 55#include <sys/kernel.h> 56#include <sys/proc_internal.h> 57#include <sys/kauth.h> 58#include <sys/msg.h> 59#include <sys/malloc.h> 60#include <mach/mach_types.h> 61 62#include <security/audit/audit.h> 63 64#include <sys/filedesc.h> 65#include <sys/file_internal.h> 66#include <sys/sysctl.h> 67#include <sys/sysproto.h> 68#include <sys/ipcs.h> 69 70#if SYSV_MSG 71 72static int msginit(void *); 73 74#define MSG_DEBUG 75#undef MSG_DEBUG_OK 76 77/* Uncomment this line to see MAC debugging output. */ 78/* #define MAC_DEBUG */ 79#if CONFIG_MACF_DEBUG 80#define MPRINTF(a) printf(a) 81#else 82#define MPRINTF(a) 83#endif 84static void msg_freehdr(struct msg *msghdr); 85 86typedef int sy_call_t(struct proc *, void *, int *); 87 88/* XXX casting to (sy_call_t *) is bogus, as usual. */ 89static sy_call_t *msgcalls[] = { 90 (sy_call_t *)msgctl, (sy_call_t *)msgget, 91 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv 92}; 93 94static int nfree_msgmaps; /* # of free map entries */ 95static short free_msgmaps; /* free map entries list head */ 96static struct msg *free_msghdrs; /* list of free msg headers */ 97char *msgpool; /* MSGMAX byte long msg buffer pool */ 98struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 99struct msg *msghdrs; /* MSGTQL msg headers */ 100struct msqid_kernel *msqids; /* MSGMNI msqid_kernel structs (wrapping user_msqid_ds structs) */ 101 102static lck_grp_t *sysv_msg_subsys_lck_grp; 103static lck_grp_attr_t *sysv_msg_subsys_lck_grp_attr; 104static lck_attr_t *sysv_msg_subsys_lck_attr; 105static lck_mtx_t sysv_msg_subsys_mutex; 106 107#define SYSV_MSG_SUBSYS_LOCK() lck_mtx_lock(&sysv_msg_subsys_mutex) 108#define SYSV_MSG_SUBSYS_UNLOCK() lck_mtx_unlock(&sysv_msg_subsys_mutex) 109 110void sysv_msg_lock_init(void); 111 112 113#ifdef __APPLE_API_PRIVATE 114 int msgmax, /* max chars in a message */ 115 msgmni, /* max message queue identifiers */ 116 msgmnb, /* max chars in a queue */ 117 msgtql, /* max messages in system */ 118 msgssz, /* size of a message segment (see notes above) */ 119 msgseg; /* number of message segments */ 120struct msginfo msginfo = { 121 MSGMAX, /* = (MSGSSZ*MSGSEG) : max chars in a message */ 122 MSGMNI, /* = 40 : max message queue identifiers */ 123 MSGMNB, /* = 2048 : max chars in a queue */ 124 MSGTQL, /* = 40 : max messages in system */ 125 MSGSSZ, /* = 8 : size of a message segment (2^N long) */ 126 MSGSEG /* = 2048 : number of message segments */ 127}; 128#endif /* __APPLE_API_PRIVATE */ 129 130/* Initialize the mutex governing access to the SysV msg subsystem */ 131__private_extern__ void 132sysv_msg_lock_init( void ) 133{ 134 sysv_msg_subsys_lck_grp_attr = lck_grp_attr_alloc_init(); 135 136 sysv_msg_subsys_lck_grp = lck_grp_alloc_init("sysv_msg_subsys_lock", sysv_msg_subsys_lck_grp_attr); 137 138 sysv_msg_subsys_lck_attr = lck_attr_alloc_init(); 139 lck_mtx_init(&sysv_msg_subsys_mutex, sysv_msg_subsys_lck_grp, sysv_msg_subsys_lck_attr); 140} 141 142static __inline__ user_time_t 143sysv_msgtime(void) 144{ 145 struct timeval tv; 146 microtime(&tv); 147 return (tv.tv_sec); 148} 149 150/* 151 * NOTE: Source and target may *NOT* overlap! (target is smaller) 152 */ 153static void 154msqid_ds_kerneltouser32(struct user_msqid_ds *in, struct user32_msqid_ds *out) 155{ 156 out->msg_perm = in->msg_perm; 157 out->msg_qnum = in->msg_qnum; 158 out->msg_cbytes = in->msg_cbytes; /* for ipcs */ 159 out->msg_qbytes = in->msg_qbytes; 160 out->msg_lspid = in->msg_lspid; 161 out->msg_lrpid = in->msg_lrpid; 162 out->msg_stime = in->msg_stime; /* XXX loss of range */ 163 out->msg_rtime = in->msg_rtime; /* XXX loss of range */ 164 out->msg_ctime = in->msg_ctime; /* XXX loss of range */ 165} 166 167static void 168msqid_ds_kerneltouser64(struct user_msqid_ds *in, struct user64_msqid_ds *out) 169{ 170 out->msg_perm = in->msg_perm; 171 out->msg_qnum = in->msg_qnum; 172 out->msg_cbytes = in->msg_cbytes; /* for ipcs */ 173 out->msg_qbytes = in->msg_qbytes; 174 out->msg_lspid = in->msg_lspid; 175 out->msg_lrpid = in->msg_lrpid; 176 out->msg_stime = in->msg_stime; /* XXX loss of range */ 177 out->msg_rtime = in->msg_rtime; /* XXX loss of range */ 178 out->msg_ctime = in->msg_ctime; /* XXX loss of range */ 179} 180 181/* 182 * NOTE: Source and target may are permitted to overlap! (source is smaller); 183 * this works because we copy fields in order from the end of the struct to 184 * the beginning. 185 */ 186static void 187msqid_ds_user32tokernel(struct user32_msqid_ds *in, struct user_msqid_ds *out) 188{ 189 out->msg_ctime = in->msg_ctime; 190 out->msg_rtime = in->msg_rtime; 191 out->msg_stime = in->msg_stime; 192 out->msg_lrpid = in->msg_lrpid; 193 out->msg_lspid = in->msg_lspid; 194 out->msg_qbytes = in->msg_qbytes; 195 out->msg_cbytes = in->msg_cbytes; /* for ipcs */ 196 out->msg_qnum = in->msg_qnum; 197 out->msg_perm = in->msg_perm; 198} 199 200static void 201msqid_ds_user64tokernel(struct user64_msqid_ds *in, struct user_msqid_ds *out) 202{ 203 out->msg_ctime = in->msg_ctime; 204 out->msg_rtime = in->msg_rtime; 205 out->msg_stime = in->msg_stime; 206 out->msg_lrpid = in->msg_lrpid; 207 out->msg_lspid = in->msg_lspid; 208 out->msg_qbytes = in->msg_qbytes; 209 out->msg_cbytes = in->msg_cbytes; /* for ipcs */ 210 out->msg_qnum = in->msg_qnum; 211 out->msg_perm = in->msg_perm; 212} 213 214/* This routine assumes the system is locked prior to calling this routine */ 215static int 216msginit(__unused void *dummy) 217{ 218 static int initted = 0; 219 register int i; 220 221 /* Lazy initialization on first system call; we don't have SYSINIT(). */ 222 if (initted) 223 return (initted); 224 225 /* 226 * msginfo.msgssz should be a power of two for efficiency reasons. 227 * It is also pretty silly if msginfo.msgssz is less than 8 228 * or greater than about 256 so ... 229 */ 230 i = 8; 231 while (i < 1024 && i != msginfo.msgssz) 232 i <<= 1; 233 if (i != msginfo.msgssz) { 234 printf("msginfo.msgssz=%d (0x%x) not a small power of 2; resetting to %d\n", msginfo.msgssz, msginfo.msgssz, MSGSSZ); 235 msginfo.msgssz = MSGSSZ; 236 } 237 238 if (msginfo.msgseg > 32767) { 239 printf("msginfo.msgseg=%d (> 32767); resetting to %d\n", msginfo.msgseg, MSGSEG); 240 msginfo.msgseg = MSGSEG; 241 } 242 243 244 /* 245 * Allocate memory for message pool, maps, headers, and queue IDs; 246 * if this fails, fail safely and leave it uninitialized (related 247 * system calls will fail). 248 */ 249 msgpool = (char *)_MALLOC(msginfo.msgmax, M_SHM, M_WAITOK); 250 if (msgpool == NULL) { 251 printf("msginit: can't allocate msgpool"); 252 goto bad; 253 } 254 MALLOC(msgmaps, struct msgmap *, 255 sizeof(struct msgmap) * msginfo.msgseg, 256 M_SHM, M_WAITOK); 257 if (msgmaps == NULL) { 258 printf("msginit: can't allocate msgmaps"); 259 goto bad; 260 } 261 262 MALLOC(msghdrs, struct msg *, 263 sizeof(struct msg) * msginfo.msgtql, 264 M_SHM, M_WAITOK); 265 if (msghdrs == NULL) { 266 printf("msginit: can't allocate msghdrs"); 267 goto bad; 268 } 269 270 MALLOC(msqids, struct msqid_kernel *, 271 sizeof(struct user_msqid_ds) * msginfo.msgmni, 272 M_SHM, M_WAITOK); 273 if (msqids == NULL) { 274 printf("msginit: can't allocate msqids"); 275 goto bad; 276 } 277 278 279 /* init msgmaps */ 280 for (i = 0; i < msginfo.msgseg; i++) { 281 if (i > 0) 282 msgmaps[i-1].next = i; 283 msgmaps[i].next = -1; /* implies entry is available */ 284 } 285 free_msgmaps = 0; 286 nfree_msgmaps = msginfo.msgseg; 287 288 289 /* init msghdrs */ 290 for (i = 0; i < msginfo.msgtql; i++) { 291 msghdrs[i].msg_type = 0; 292 if (i > 0) 293 msghdrs[i-1].msg_next = &msghdrs[i]; 294 msghdrs[i].msg_next = NULL; 295#if CONFIG_MACF 296 mac_sysvmsg_label_init(&msghdrs[i]); 297#endif 298 } 299 free_msghdrs = &msghdrs[0]; 300 301 /* init msqids */ 302 for (i = 0; i < msginfo.msgmni; i++) { 303 msqids[i].u.msg_qbytes = 0; /* implies entry is available */ 304 msqids[i].u.msg_perm._seq = 0; /* reset to a known value */ 305 msqids[i].u.msg_perm.mode = 0; 306#if CONFIG_MACF 307 mac_sysvmsq_label_init(&msqids[i]); 308#endif 309 } 310 311 initted = 1; 312bad: 313 if (!initted) { 314 if (msgpool != NULL) 315 _FREE(msgpool, M_SHM); 316 if (msgmaps != NULL) 317 FREE(msgmaps, M_SHM); 318 if (msghdrs != NULL) 319 FREE(msghdrs, M_SHM); 320 if (msqids != NULL) 321 FREE(msqids, M_SHM); 322 } 323 return (initted); 324} 325 326/* 327 * msgsys 328 * 329 * Entry point for all MSG calls: msgctl, msgget, msgsnd, msgrcv 330 * 331 * Parameters: p Process requesting the call 332 * uap User argument descriptor (see below) 333 * retval Return value of the selected msg call 334 * 335 * Indirect parameters: uap->which msg call to invoke (index in array of msg calls) 336 * uap->a2 User argument descriptor 337 * 338 * Returns: 0 Success 339 * !0 Not success 340 * 341 * Implicit returns: retval Return value of the selected msg call 342 * 343 * DEPRECATED: This interface should not be used to call the other MSG 344 * functions (msgctl, msgget, msgsnd, msgrcv). The correct 345 * usage is to call the other MSG functions directly. 346 * 347 */ 348int 349msgsys(struct proc *p, struct msgsys_args *uap, int32_t *retval) 350{ 351 if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) 352 return (EINVAL); 353 return ((*msgcalls[uap->which])(p, &uap->a2, retval)); 354} 355 356static void 357msg_freehdr(struct msg *msghdr) 358{ 359 while (msghdr->msg_ts > 0) { 360 short next; 361 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 362 panic("msghdr->msg_spot out of range"); 363 next = msgmaps[msghdr->msg_spot].next; 364 msgmaps[msghdr->msg_spot].next = free_msgmaps; 365 free_msgmaps = msghdr->msg_spot; 366 nfree_msgmaps++; 367 msghdr->msg_spot = next; 368 if (msghdr->msg_ts >= msginfo.msgssz) 369 msghdr->msg_ts -= msginfo.msgssz; 370 else 371 msghdr->msg_ts = 0; 372 } 373 if (msghdr->msg_spot != -1) 374 panic("msghdr->msg_spot != -1"); 375 msghdr->msg_next = free_msghdrs; 376 free_msghdrs = msghdr; 377#if CONFIG_MACF 378 mac_sysvmsg_label_recycle(msghdr); 379#endif 380 /* 381 * Notify waiters that there are free message headers and segments 382 * now available. 383 */ 384 wakeup((caddr_t)&free_msghdrs); 385} 386 387int 388msgctl(struct proc *p, struct msgctl_args *uap, int32_t *retval) 389{ 390 int msqid = uap->msqid; 391 int cmd = uap->cmd; 392 kauth_cred_t cred = kauth_cred_get(); 393 int rval, eval; 394 struct user_msqid_ds msqbuf; 395 struct msqid_kernel *msqptr; 396 397 SYSV_MSG_SUBSYS_LOCK(); 398 399 if (!msginit(0)) { 400 eval = ENOMEM; 401 goto msgctlout; 402 } 403 404#ifdef MSG_DEBUG_OK 405 printf("call to msgctl(%d, %d, 0x%qx)\n", msqid, cmd, uap->buf); 406#endif 407 408 AUDIT_ARG(svipc_cmd, cmd); 409 AUDIT_ARG(svipc_id, msqid); 410 msqid = IPCID_TO_IX(msqid); 411 412 if (msqid < 0 || msqid >= msginfo.msgmni) { 413#ifdef MSG_DEBUG_OK 414 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 415 msginfo.msgmni); 416#endif 417 eval = EINVAL; 418 goto msgctlout; 419 } 420 421 msqptr = &msqids[msqid]; 422 423 if (msqptr->u.msg_qbytes == 0) { 424#ifdef MSG_DEBUG_OK 425 printf("no such msqid\n"); 426#endif 427 eval = EINVAL; 428 goto msgctlout; 429 } 430 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { 431#ifdef MSG_DEBUG_OK 432 printf("wrong sequence number\n"); 433#endif 434 eval = EINVAL; 435 goto msgctlout; 436 } 437#if CONFIG_MACF 438 eval = mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr, cmd); 439 if (eval) 440 goto msgctlout; 441#endif 442 443 eval = 0; 444 rval = 0; 445 446 switch (cmd) { 447 448 case IPC_RMID: 449 { 450 struct msg *msghdr; 451 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) 452 goto msgctlout; 453#if CONFIG_MACF 454 /* 455 * Check that the thread has MAC access permissions to 456 * individual msghdrs. Note: We need to do this in a 457 * separate loop because the actual loop alters the 458 * msq/msghdr info as it progresses, and there is no going 459 * back if half the way through we discover that the 460 * thread cannot free a certain msghdr. The msq will get 461 * into an inconsistent state. 462 */ 463 for (msghdr = msqptr->u.msg_first; msghdr != NULL; 464 msghdr = msghdr->msg_next) { 465 eval = mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr); 466 if (eval) 467 goto msgctlout; 468 } 469#endif 470 /* Free the message headers */ 471 msghdr = msqptr->u.msg_first; 472 while (msghdr != NULL) { 473 struct msg *msghdr_tmp; 474 475 /* Free the segments of each message */ 476 msqptr->u.msg_cbytes -= msghdr->msg_ts; 477 msqptr->u.msg_qnum--; 478 msghdr_tmp = msghdr; 479 msghdr = msghdr->msg_next; 480 msg_freehdr(msghdr_tmp); 481 } 482 483 if (msqptr->u.msg_cbytes != 0) 484 panic("msg_cbytes is messed up"); 485 if (msqptr->u.msg_qnum != 0) 486 panic("msg_qnum is messed up"); 487 488 msqptr->u.msg_qbytes = 0; /* Mark it as free */ 489#if CONFIG_MACF 490 mac_sysvmsq_label_recycle(msqptr); 491#endif 492 493 wakeup((caddr_t)msqptr); 494 } 495 496 break; 497 498 case IPC_SET: 499 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) 500 goto msgctlout; 501 502 SYSV_MSG_SUBSYS_UNLOCK(); 503 504 if (IS_64BIT_PROCESS(p)) { 505 struct user64_msqid_ds tmpds; 506 eval = copyin(uap->buf, &tmpds, sizeof(tmpds)); 507 508 msqid_ds_user64tokernel(&tmpds, &msqbuf); 509 } else { 510 struct user32_msqid_ds tmpds; 511 512 eval = copyin(uap->buf, &tmpds, sizeof(tmpds)); 513 514 msqid_ds_user32tokernel(&tmpds, &msqbuf); 515 } 516 if (eval) 517 return(eval); 518 519 SYSV_MSG_SUBSYS_LOCK(); 520 521 if (msqbuf.msg_qbytes > msqptr->u.msg_qbytes) { 522 eval = suser(cred, &p->p_acflag); 523 if (eval) 524 goto msgctlout; 525 } 526 527 528 /* compare (msglen_t) value against restrict (int) value */ 529 if (msqbuf.msg_qbytes > (user_msglen_t)msginfo.msgmnb) { 530#ifdef MSG_DEBUG_OK 531 printf("can't increase msg_qbytes beyond %d (truncating)\n", 532 msginfo.msgmnb); 533#endif 534 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 535 } 536 if (msqbuf.msg_qbytes == 0) { 537#ifdef MSG_DEBUG_OK 538 printf("can't reduce msg_qbytes to 0\n"); 539#endif 540 eval = EINVAL; 541 goto msgctlout; 542 } 543 msqptr->u.msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 544 msqptr->u.msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 545 msqptr->u.msg_perm.mode = (msqptr->u.msg_perm.mode & ~0777) | 546 (msqbuf.msg_perm.mode & 0777); 547 msqptr->u.msg_qbytes = msqbuf.msg_qbytes; 548 msqptr->u.msg_ctime = sysv_msgtime(); 549 break; 550 551 case IPC_STAT: 552 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_R))) { 553#ifdef MSG_DEBUG_OK 554 printf("requester doesn't have read access\n"); 555#endif 556 goto msgctlout; 557 } 558 559 SYSV_MSG_SUBSYS_UNLOCK(); 560 if (IS_64BIT_PROCESS(p)) { 561 struct user64_msqid_ds msqid_ds64; 562 msqid_ds_kerneltouser64(&msqptr->u, &msqid_ds64); 563 eval = copyout(&msqid_ds64, uap->buf, sizeof(msqid_ds64)); 564 } else { 565 struct user32_msqid_ds msqid_ds32; 566 msqid_ds_kerneltouser32(&msqptr->u, &msqid_ds32); 567 eval = copyout(&msqid_ds32, uap->buf, sizeof(msqid_ds32)); 568 } 569 SYSV_MSG_SUBSYS_LOCK(); 570 break; 571 572 default: 573#ifdef MSG_DEBUG_OK 574 printf("invalid command %d\n", cmd); 575#endif 576 eval = EINVAL; 577 goto msgctlout; 578 } 579 580 if (eval == 0) 581 *retval = rval; 582msgctlout: 583 SYSV_MSG_SUBSYS_UNLOCK(); 584 return(eval); 585} 586 587int 588msgget(__unused struct proc *p, struct msgget_args *uap, int32_t *retval) 589{ 590 int msqid, eval; 591 int key = uap->key; 592 int msgflg = uap->msgflg; 593 kauth_cred_t cred = kauth_cred_get(); 594 struct msqid_kernel *msqptr = NULL; 595 596 SYSV_MSG_SUBSYS_LOCK(); 597 598 if (!msginit(0)) { 599 eval = ENOMEM; 600 goto msggetout; 601 } 602 603#ifdef MSG_DEBUG_OK 604 printf("msgget(0x%x, 0%o)\n", key, msgflg); 605#endif 606 607 if (key != IPC_PRIVATE) { 608 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 609 msqptr = &msqids[msqid]; 610 if (msqptr->u.msg_qbytes != 0 && 611 msqptr->u.msg_perm._key == key) 612 break; 613 } 614 if (msqid < msginfo.msgmni) { 615#ifdef MSG_DEBUG_OK 616 printf("found public key\n"); 617#endif 618 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 619#ifdef MSG_DEBUG_OK 620 printf("not exclusive\n"); 621#endif 622 eval = EEXIST; 623 goto msggetout; 624 } 625 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, msgflg & 0700 ))) { 626#ifdef MSG_DEBUG_OK 627 printf("requester doesn't have 0%o access\n", 628 msgflg & 0700); 629#endif 630 goto msggetout; 631 } 632#if CONFIG_MACF 633 eval = mac_sysvmsq_check_msqget(cred, msqptr); 634 if (eval) 635 goto msggetout; 636#endif 637 goto found; 638 } 639 } 640 641#ifdef MSG_DEBUG_OK 642 printf("need to allocate the user_msqid_ds\n"); 643#endif 644 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 645 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 646 /* 647 * Look for an unallocated and unlocked user_msqid_ds. 648 * user_msqid_ds's can be locked by msgsnd or msgrcv 649 * while they are copying the message in/out. We 650 * can't re-use the entry until they release it. 651 */ 652 msqptr = &msqids[msqid]; 653 if (msqptr->u.msg_qbytes == 0 && 654 (msqptr->u.msg_perm.mode & MSG_LOCKED) == 0) 655 break; 656 } 657 if (msqid == msginfo.msgmni) { 658#ifdef MSG_DEBUG_OK 659 printf("no more user_msqid_ds's available\n"); 660#endif 661 eval = ENOSPC; 662 goto msggetout; 663 } 664#ifdef MSG_DEBUG_OK 665 printf("msqid %d is available\n", msqid); 666#endif 667 msqptr->u.msg_perm._key = key; 668 msqptr->u.msg_perm.cuid = kauth_cred_getuid(cred); 669 msqptr->u.msg_perm.uid = kauth_cred_getuid(cred); 670 msqptr->u.msg_perm.cgid = kauth_cred_getgid(cred); 671 msqptr->u.msg_perm.gid = kauth_cred_getgid(cred); 672 msqptr->u.msg_perm.mode = (msgflg & 0777); 673 /* Make sure that the returned msqid is unique */ 674 msqptr->u.msg_perm._seq++; 675 msqptr->u.msg_first = NULL; 676 msqptr->u.msg_last = NULL; 677 msqptr->u.msg_cbytes = 0; 678 msqptr->u.msg_qnum = 0; 679 msqptr->u.msg_qbytes = msginfo.msgmnb; 680 msqptr->u.msg_lspid = 0; 681 msqptr->u.msg_lrpid = 0; 682 msqptr->u.msg_stime = 0; 683 msqptr->u.msg_rtime = 0; 684 msqptr->u.msg_ctime = sysv_msgtime(); 685#if CONFIG_MACF 686 mac_sysvmsq_label_associate(cred, msqptr); 687#endif 688 } else { 689#ifdef MSG_DEBUG_OK 690 printf("didn't find it and wasn't asked to create it\n"); 691#endif 692 eval = ENOENT; 693 goto msggetout; 694 } 695 696found: 697 /* Construct the unique msqid */ 698 *retval = IXSEQ_TO_IPCID(msqid, msqptr->u.msg_perm); 699 AUDIT_ARG(svipc_id, *retval); 700 eval = 0; 701msggetout: 702 SYSV_MSG_SUBSYS_UNLOCK(); 703 return(eval); 704} 705 706 707int 708msgsnd(struct proc *p, struct msgsnd_args *uap, int32_t *retval) 709{ 710 __pthread_testcancel(1); 711 return(msgsnd_nocancel(p, (struct msgsnd_nocancel_args *)uap, retval)); 712} 713 714int 715msgsnd_nocancel(struct proc *p, struct msgsnd_nocancel_args *uap, int32_t *retval) 716{ 717 int msqid = uap->msqid; 718 user_addr_t user_msgp = uap->msgp; 719 size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */ 720 int msgflg = uap->msgflg; 721 int segs_needed, eval; 722 struct msqid_kernel *msqptr; 723 struct msg *msghdr; 724 short next; 725 user_long_t msgtype; 726 727 728 SYSV_MSG_SUBSYS_LOCK(); 729 730 if (!msginit(0)) { 731 eval = ENOMEM; 732 goto msgsndout; 733 } 734 735#ifdef MSG_DEBUG_OK 736 printf("call to msgsnd(%d, 0x%qx, %ld, %d)\n", msqid, user_msgp, msgsz, 737 msgflg); 738#endif 739 740 AUDIT_ARG(svipc_id, msqid); 741 msqid = IPCID_TO_IX(msqid); 742 743 if (msqid < 0 || msqid >= msginfo.msgmni) { 744#ifdef MSG_DEBUG_OK 745 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 746 msginfo.msgmni); 747#endif 748 eval = EINVAL; 749 goto msgsndout; 750 } 751 752 msqptr = &msqids[msqid]; 753 if (msqptr->u.msg_qbytes == 0) { 754#ifdef MSG_DEBUG_OK 755 printf("no such message queue id\n"); 756#endif 757 eval = EINVAL; 758 goto msgsndout; 759 } 760 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { 761#ifdef MSG_DEBUG_OK 762 printf("wrong sequence number\n"); 763#endif 764 eval = EINVAL; 765 goto msgsndout; 766 } 767 768 if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_W))) { 769#ifdef MSG_DEBUG_OK 770 printf("requester doesn't have write access\n"); 771#endif 772 goto msgsndout; 773 } 774 775#if CONFIG_MACF 776 eval = mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr); 777 if (eval) 778 goto msgsndout; 779#endif 780 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 781#ifdef MSG_DEBUG_OK 782 printf("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 783 segs_needed); 784#endif 785 786 /* 787 * If we suffer resource starvation, we will sleep in this loop and 788 * wait for more resources to become available. This is a loop to 789 * ensure reacquisition of the mutex following any sleep, since there 790 * are multiple resources under contention. 791 */ 792 for (;;) { 793 void *blocking_resource = NULL; 794 795 /* 796 * Check that we have not had the maximum message size change 797 * out from under us and render our message invalid while we 798 * slept waiting for some resource. 799 */ 800 if (msgsz > msqptr->u.msg_qbytes) { 801#ifdef MSG_DEBUG_OK 802 printf("msgsz > msqptr->msg_qbytes\n"); 803#endif 804 eval = EINVAL; 805 goto msgsndout; 806 } 807 808 /* 809 * If the user_msqid_ds is already locked, we need to sleep on 810 * the queue until it's unlocked. 811 */ 812 if (msqptr->u.msg_perm.mode & MSG_LOCKED) { 813#ifdef MSG_DEBUG_OK 814 printf("msqid is locked\n"); 815#endif 816 blocking_resource = msqptr; 817 } 818 819 /* 820 * If our message plus the messages already in the queue would 821 * cause us to exceed the maximum number of bytes wer are 822 * permitted to queue, then block on the queue until it drains. 823 */ 824 if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) { 825#ifdef MSG_DEBUG_OK 826 printf("msgsz + msg_cbytes > msg_qbytes\n"); 827#endif 828 blocking_resource = msqptr; 829 } 830 831 /* 832 * Both message maps and message headers are protected by 833 * sleeping on the address of the pointer to the list of free 834 * message headers, since they are allocated and freed in 835 * tandem. 836 */ 837 if (segs_needed > nfree_msgmaps) { 838#ifdef MSG_DEBUG_OK 839 printf("segs_needed > nfree_msgmaps\n"); 840#endif 841 blocking_resource = &free_msghdrs; 842 } 843 if (free_msghdrs == NULL) { 844#ifdef MSG_DEBUG_OK 845 printf("no more msghdrs\n"); 846#endif 847 blocking_resource = &free_msghdrs; 848 } 849 850 if (blocking_resource != NULL) { 851 int we_own_it; 852 853 if ((msgflg & IPC_NOWAIT) != 0) { 854#ifdef MSG_DEBUG_OK 855 printf("need more resources but caller doesn't want to wait\n"); 856#endif 857 eval = EAGAIN; 858 goto msgsndout; 859 } 860 861 if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) { 862#ifdef MSG_DEBUG_OK 863 printf("we don't own the user_msqid_ds\n"); 864#endif 865 we_own_it = 0; 866 } else { 867 /* Force later arrivals to wait for our 868 request */ 869#ifdef MSG_DEBUG_OK 870 printf("we own the user_msqid_ds\n"); 871#endif 872 msqptr->u.msg_perm.mode |= MSG_LOCKED; 873 we_own_it = 1; 874 } 875#ifdef MSG_DEBUG_OK 876 printf("goodnight\n"); 877#endif 878 eval = msleep(blocking_resource, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH, 879 "msgwait", 0); 880#ifdef MSG_DEBUG_OK 881 printf("good morning, eval=%d\n", eval); 882#endif 883 if (we_own_it) 884 msqptr->u.msg_perm.mode &= ~MSG_LOCKED; 885 if (eval != 0) { 886#ifdef MSG_DEBUG_OK 887 printf("msgsnd: interrupted system call\n"); 888#endif 889 eval = EINTR; 890 goto msgsndout; 891 } 892 893 /* 894 * Make sure that the msq queue still exists 895 */ 896 897 if (msqptr->u.msg_qbytes == 0) { 898#ifdef MSG_DEBUG_OK 899 printf("msqid deleted\n"); 900#endif 901 eval = EIDRM; 902 goto msgsndout; 903 904 } 905 906 } else { 907#ifdef MSG_DEBUG_OK 908 printf("got all the resources that we need\n"); 909#endif 910 break; 911 } 912 } 913 914 /* 915 * We have the resources that we need. 916 * Make sure! 917 */ 918 919 if (msqptr->u.msg_perm.mode & MSG_LOCKED) 920 panic("msg_perm.mode & MSG_LOCKED"); 921 if (segs_needed > nfree_msgmaps) 922 panic("segs_needed > nfree_msgmaps"); 923 if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) 924 panic("msgsz + msg_cbytes > msg_qbytes"); 925 if (free_msghdrs == NULL) 926 panic("no more msghdrs"); 927 928 /* 929 * Re-lock the user_msqid_ds in case we page-fault when copying in 930 * the message 931 */ 932 if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) 933 panic("user_msqid_ds is already locked"); 934 msqptr->u.msg_perm.mode |= MSG_LOCKED; 935 936 /* 937 * Allocate a message header 938 */ 939 msghdr = free_msghdrs; 940 free_msghdrs = msghdr->msg_next; 941 msghdr->msg_spot = -1; 942 msghdr->msg_ts = msgsz; 943 944#if CONFIG_MACF 945 mac_sysvmsg_label_associate(kauth_cred_get(), msqptr, msghdr); 946#endif 947 /* 948 * Allocate space for the message 949 */ 950 951 while (segs_needed > 0) { 952 if (nfree_msgmaps <= 0) 953 panic("not enough msgmaps"); 954 if (free_msgmaps == -1) 955 panic("nil free_msgmaps"); 956 next = free_msgmaps; 957 if (next <= -1) 958 panic("next too low #1"); 959 if (next >= msginfo.msgseg) 960 panic("next out of range #1"); 961#ifdef MSG_DEBUG_OK 962 printf("allocating segment %d to message\n", next); 963#endif 964 free_msgmaps = msgmaps[next].next; 965 nfree_msgmaps--; 966 msgmaps[next].next = msghdr->msg_spot; 967 msghdr->msg_spot = next; 968 segs_needed--; 969 } 970 971 /* 972 * Copy in the message type. For a 64 bit process, this is 64 bits, 973 * but we only ever use the low 32 bits, so the cast is OK. 974 */ 975 if (IS_64BIT_PROCESS(p)) { 976 SYSV_MSG_SUBSYS_UNLOCK(); 977 eval = copyin(user_msgp, &msgtype, sizeof(msgtype)); 978 SYSV_MSG_SUBSYS_LOCK(); 979 msghdr->msg_type = CAST_DOWN(long,msgtype); 980 user_msgp = user_msgp + sizeof(msgtype); /* ptr math */ 981 } else { 982 SYSV_MSG_SUBSYS_UNLOCK(); 983 int32_t msg_type32; 984 eval = copyin(user_msgp, &msg_type32, sizeof(msg_type32)); 985 msghdr->msg_type = msg_type32; 986 SYSV_MSG_SUBSYS_LOCK(); 987 user_msgp = user_msgp + sizeof(msg_type32); /* ptr math */ 988 } 989 990 if (eval != 0) { 991#ifdef MSG_DEBUG_OK 992 printf("error %d copying the message type\n", eval); 993#endif 994 msg_freehdr(msghdr); 995 msqptr->u.msg_perm.mode &= ~MSG_LOCKED; 996 wakeup((caddr_t)msqptr); 997 goto msgsndout; 998 } 999 1000 1001 /* 1002 * Validate the message type 1003 */ 1004 if (msghdr->msg_type < 1) { 1005 msg_freehdr(msghdr); 1006 msqptr->u.msg_perm.mode &= ~MSG_LOCKED; 1007 wakeup((caddr_t)msqptr); 1008#ifdef MSG_DEBUG_OK 1009 printf("mtype (%ld) < 1\n", msghdr->msg_type); 1010#endif 1011 eval = EINVAL; 1012 goto msgsndout; 1013 } 1014 1015 /* 1016 * Copy in the message body 1017 */ 1018 next = msghdr->msg_spot; 1019 while (msgsz > 0) { 1020 size_t tlen; 1021 /* compare input (size_t) value against restrict (int) value */ 1022 if (msgsz > (size_t)msginfo.msgssz) 1023 tlen = msginfo.msgssz; 1024 else 1025 tlen = msgsz; 1026 if (next <= -1) 1027 panic("next too low #2"); 1028 if (next >= msginfo.msgseg) 1029 panic("next out of range #2"); 1030 1031 SYSV_MSG_SUBSYS_UNLOCK(); 1032 eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen); 1033 SYSV_MSG_SUBSYS_LOCK(); 1034 1035 if (eval != 0) { 1036#ifdef MSG_DEBUG_OK 1037 printf("error %d copying in message segment\n", eval); 1038#endif 1039 msg_freehdr(msghdr); 1040 msqptr->u.msg_perm.mode &= ~MSG_LOCKED; 1041 wakeup((caddr_t)msqptr); 1042 1043 goto msgsndout; 1044 } 1045 msgsz -= tlen; 1046 user_msgp = user_msgp + tlen; /* ptr math */ 1047 next = msgmaps[next].next; 1048 } 1049 if (next != -1) 1050 panic("didn't use all the msg segments"); 1051 1052 /* 1053 * We've got the message. Unlock the user_msqid_ds. 1054 */ 1055 1056 msqptr->u.msg_perm.mode &= ~MSG_LOCKED; 1057 1058 /* 1059 * Make sure that the user_msqid_ds is still allocated. 1060 */ 1061 1062 if (msqptr->u.msg_qbytes == 0) { 1063 msg_freehdr(msghdr); 1064 wakeup((caddr_t)msqptr); 1065 /* The SVID says to return EIDRM. */ 1066#ifdef EIDRM 1067 eval = EIDRM; 1068#else 1069 /* Unfortunately, BSD doesn't define that code yet! */ 1070 eval = EINVAL; 1071#endif 1072 goto msgsndout; 1073 } 1074 1075#if CONFIG_MACF 1076 /* 1077 * Note: Since the task/thread allocates the msghdr and usually 1078 * primes it with its own MAC label, for a majority of policies, it 1079 * won't be necessary to check whether the msghdr has access 1080 * permissions to the msgq. The mac_sysvmsq_check_msqsnd check would 1081 * suffice in that case. However, this hook may be required where 1082 * individual policies derive a non-identical label for the msghdr 1083 * from the current thread label and may want to check the msghdr 1084 * enqueue permissions, along with read/write permissions to the 1085 * msgq. 1086 */ 1087 eval = mac_sysvmsq_check_enqueue(kauth_cred_get(), msghdr, msqptr); 1088 if (eval) { 1089 msg_freehdr(msghdr); 1090 wakeup((caddr_t) msqptr); 1091 goto msgsndout; 1092 } 1093#endif 1094 /* 1095 * Put the message into the queue 1096 */ 1097 1098 if (msqptr->u.msg_first == NULL) { 1099 msqptr->u.msg_first = msghdr; 1100 msqptr->u.msg_last = msghdr; 1101 } else { 1102 msqptr->u.msg_last->msg_next = msghdr; 1103 msqptr->u.msg_last = msghdr; 1104 } 1105 msqptr->u.msg_last->msg_next = NULL; 1106 1107 msqptr->u.msg_cbytes += msghdr->msg_ts; 1108 msqptr->u.msg_qnum++; 1109 msqptr->u.msg_lspid = p->p_pid; 1110 msqptr->u.msg_stime = sysv_msgtime(); 1111 1112 wakeup((caddr_t)msqptr); 1113 *retval = 0; 1114 eval = 0; 1115 1116msgsndout: 1117 SYSV_MSG_SUBSYS_UNLOCK(); 1118 return(eval); 1119} 1120 1121 1122int 1123msgrcv(struct proc *p, struct msgrcv_args *uap, user_ssize_t *retval) 1124{ 1125 __pthread_testcancel(1); 1126 return(msgrcv_nocancel(p, (struct msgrcv_nocancel_args *)uap, retval)); 1127} 1128 1129int 1130msgrcv_nocancel(struct proc *p, struct msgrcv_nocancel_args *uap, user_ssize_t *retval) 1131{ 1132 int msqid = uap->msqid; 1133 user_addr_t user_msgp = uap->msgp; 1134 size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */ 1135 long msgtyp = (long)uap->msgtyp; /* limit to 32 bits */ 1136 int msgflg = uap->msgflg; 1137 size_t len; 1138 struct msqid_kernel *msqptr; 1139 struct msg *msghdr; 1140 int eval; 1141 short next; 1142 user_long_t msgtype; 1143 int32_t msg_type32; 1144 1145 SYSV_MSG_SUBSYS_LOCK(); 1146 1147 if (!msginit(0)) { 1148 eval = ENOMEM; 1149 goto msgrcvout; 1150 } 1151 1152#ifdef MSG_DEBUG_OK 1153 printf("call to msgrcv(%d, 0x%qx, %ld, %ld, %d)\n", msqid, user_msgp, 1154 msgsz, msgtyp, msgflg); 1155#endif 1156 1157 AUDIT_ARG(svipc_id, msqid); 1158 msqid = IPCID_TO_IX(msqid); 1159 1160 if (msqid < 0 || msqid >= msginfo.msgmni) { 1161#ifdef MSG_DEBUG_OK 1162 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 1163 msginfo.msgmni); 1164#endif 1165 eval = EINVAL; 1166 goto msgrcvout; 1167 } 1168 1169 msqptr = &msqids[msqid]; 1170 if (msqptr->u.msg_qbytes == 0) { 1171#ifdef MSG_DEBUG_OK 1172 printf("no such message queue id\n"); 1173#endif 1174 eval = EINVAL; 1175 goto msgrcvout; 1176 } 1177 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { 1178#ifdef MSG_DEBUG_OK 1179 printf("wrong sequence number\n"); 1180#endif 1181 eval = EINVAL; 1182 goto msgrcvout; 1183 } 1184 1185 if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_R))) { 1186#ifdef MSG_DEBUG_OK 1187 printf("requester doesn't have read access\n"); 1188#endif 1189 goto msgrcvout; 1190 } 1191 1192#if CONFIG_MACF 1193 eval = mac_sysvmsq_check_msqrcv(kauth_cred_get(), msqptr); 1194 if (eval) 1195 goto msgrcvout; 1196#endif 1197 msghdr = NULL; 1198 while (msghdr == NULL) { 1199 if (msgtyp == 0) { 1200 msghdr = msqptr->u.msg_first; 1201 if (msghdr != NULL) { 1202 if (msgsz < msghdr->msg_ts && 1203 (msgflg & MSG_NOERROR) == 0) { 1204#ifdef MSG_DEBUG_OK 1205 printf("first message on the queue is too big (want %ld, got %d)\n", 1206 msgsz, msghdr->msg_ts); 1207#endif 1208 eval = E2BIG; 1209 goto msgrcvout; 1210 } 1211#if CONFIG_MACF 1212 eval = mac_sysvmsq_check_msgrcv(kauth_cred_get(), 1213 msghdr); 1214 if (eval) 1215 goto msgrcvout; 1216#endif 1217 if (msqptr->u.msg_first == msqptr->u.msg_last) { 1218 msqptr->u.msg_first = NULL; 1219 msqptr->u.msg_last = NULL; 1220 } else { 1221 msqptr->u.msg_first = msghdr->msg_next; 1222 if (msqptr->u.msg_first == NULL) 1223 panic("msg_first/last messed up #1"); 1224 } 1225 } 1226 } else { 1227 struct msg *previous; 1228 struct msg **prev; 1229 1230 previous = NULL; 1231 prev = &(msqptr->u.msg_first); 1232 while ((msghdr = *prev) != NULL) { 1233 /* 1234 * Is this message's type an exact match or is 1235 * this message's type less than or equal to 1236 * the absolute value of a negative msgtyp? 1237 * Note that the second half of this test can 1238 * NEVER be true if msgtyp is positive since 1239 * msg_type is always positive! 1240 */ 1241 1242 if (msgtyp == msghdr->msg_type || 1243 msghdr->msg_type <= -msgtyp) { 1244#ifdef MSG_DEBUG_OK 1245 printf("found message type %ld, requested %ld\n", 1246 msghdr->msg_type, msgtyp); 1247#endif 1248 if (msgsz < msghdr->msg_ts && 1249 (msgflg & MSG_NOERROR) == 0) { 1250#ifdef MSG_DEBUG_OK 1251 printf("requested message on the queue is too big (want %ld, got %d)\n", 1252 msgsz, msghdr->msg_ts); 1253#endif 1254 eval = E2BIG; 1255 goto msgrcvout; 1256 } 1257#if CONFIG_MACF 1258 eval = mac_sysvmsq_check_msgrcv( 1259 kauth_cred_get(), msghdr); 1260 if (eval) 1261 goto msgrcvout; 1262#endif 1263 *prev = msghdr->msg_next; 1264 if (msghdr == msqptr->u.msg_last) { 1265 if (previous == NULL) { 1266 if (prev != 1267 &msqptr->u.msg_first) 1268 panic("msg_first/last messed up #2"); 1269 msqptr->u.msg_first = 1270 NULL; 1271 msqptr->u.msg_last = 1272 NULL; 1273 } else { 1274 if (prev == 1275 &msqptr->u.msg_first) 1276 panic("msg_first/last messed up #3"); 1277 msqptr->u.msg_last = 1278 previous; 1279 } 1280 } 1281 break; 1282 } 1283 previous = msghdr; 1284 prev = &(msghdr->msg_next); 1285 } 1286 } 1287 1288 /* 1289 * We've either extracted the msghdr for the appropriate 1290 * message or there isn't one. 1291 * If there is one then bail out of this loop. 1292 */ 1293 1294 if (msghdr != NULL) 1295 break; 1296 1297 /* 1298 * Hmph! No message found. Does the user want to wait? 1299 */ 1300 1301 if ((msgflg & IPC_NOWAIT) != 0) { 1302#ifdef MSG_DEBUG_OK 1303 printf("no appropriate message found (msgtyp=%ld)\n", 1304 msgtyp); 1305#endif 1306 /* The SVID says to return ENOMSG. */ 1307#ifdef ENOMSG 1308 eval = ENOMSG; 1309#else 1310 /* Unfortunately, BSD doesn't define that code yet! */ 1311 eval = EAGAIN; 1312#endif 1313 goto msgrcvout; 1314 } 1315 1316 /* 1317 * Wait for something to happen 1318 */ 1319 1320#ifdef MSG_DEBUG_OK 1321 printf("msgrcv: goodnight\n"); 1322#endif 1323 eval = msleep((caddr_t)msqptr, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH, "msgwait", 1324 0); 1325#ifdef MSG_DEBUG_OK 1326 printf("msgrcv: good morning (eval=%d)\n", eval); 1327#endif 1328 1329 if (eval != 0) { 1330#ifdef MSG_DEBUG_OK 1331 printf("msgsnd: interrupted system call\n"); 1332#endif 1333 eval = EINTR; 1334 goto msgrcvout; 1335 } 1336 1337 /* 1338 * Make sure that the msq queue still exists 1339 */ 1340 1341 if (msqptr->u.msg_qbytes == 0 || 1342 msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { 1343#ifdef MSG_DEBUG_OK 1344 printf("msqid deleted\n"); 1345#endif 1346 /* The SVID says to return EIDRM. */ 1347#ifdef EIDRM 1348 eval = EIDRM; 1349#else 1350 /* Unfortunately, BSD doesn't define that code yet! */ 1351 eval = EINVAL; 1352#endif 1353 goto msgrcvout; 1354 } 1355 } 1356 1357 /* 1358 * Return the message to the user. 1359 * 1360 * First, do the bookkeeping (before we risk being interrupted). 1361 */ 1362 1363 msqptr->u.msg_cbytes -= msghdr->msg_ts; 1364 msqptr->u.msg_qnum--; 1365 msqptr->u.msg_lrpid = p->p_pid; 1366 msqptr->u.msg_rtime = sysv_msgtime(); 1367 1368 /* 1369 * Make msgsz the actual amount that we'll be returning. 1370 * Note that this effectively truncates the message if it is too long 1371 * (since msgsz is never increased). 1372 */ 1373 1374#ifdef MSG_DEBUG_OK 1375 printf("found a message, msgsz=%ld, msg_ts=%d\n", msgsz, 1376 msghdr->msg_ts); 1377#endif 1378 if (msgsz > msghdr->msg_ts) 1379 msgsz = msghdr->msg_ts; 1380 1381 /* 1382 * Return the type to the user. 1383 */ 1384 1385 /* 1386 * Copy out the message type. For a 64 bit process, this is 64 bits, 1387 * but we only ever use the low 32 bits, so the cast is OK. 1388 */ 1389 if (IS_64BIT_PROCESS(p)) { 1390 msgtype = msghdr->msg_type; 1391 SYSV_MSG_SUBSYS_UNLOCK(); 1392 eval = copyout(&msgtype, user_msgp, sizeof(msgtype)); 1393 SYSV_MSG_SUBSYS_LOCK(); 1394 user_msgp = user_msgp + sizeof(msgtype); /* ptr math */ 1395 } else { 1396 msg_type32 = msghdr->msg_type; 1397 SYSV_MSG_SUBSYS_UNLOCK(); 1398 eval = copyout(&msg_type32, user_msgp, sizeof(msg_type32)); 1399 SYSV_MSG_SUBSYS_LOCK(); 1400 user_msgp = user_msgp + sizeof(msg_type32); /* ptr math */ 1401 } 1402 1403 if (eval != 0) { 1404#ifdef MSG_DEBUG_OK 1405 printf("error (%d) copying out message type\n", eval); 1406#endif 1407 msg_freehdr(msghdr); 1408 wakeup((caddr_t)msqptr); 1409 1410 goto msgrcvout; 1411 } 1412 1413 1414 /* 1415 * Return the segments to the user 1416 */ 1417 1418 next = msghdr->msg_spot; 1419 for (len = 0; len < msgsz; len += msginfo.msgssz) { 1420 size_t tlen; 1421 1422 /* compare input (size_t) value against restrict (int) value */ 1423 if (msgsz > (size_t)msginfo.msgssz) 1424 tlen = msginfo.msgssz; 1425 else 1426 tlen = msgsz; 1427 if (next <= -1) 1428 panic("next too low #3"); 1429 if (next >= msginfo.msgseg) 1430 panic("next out of range #3"); 1431 SYSV_MSG_SUBSYS_UNLOCK(); 1432 eval = copyout(&msgpool[next * msginfo.msgssz], 1433 user_msgp, tlen); 1434 SYSV_MSG_SUBSYS_LOCK(); 1435 if (eval != 0) { 1436#ifdef MSG_DEBUG_OK 1437 printf("error (%d) copying out message segment\n", 1438 eval); 1439#endif 1440 msg_freehdr(msghdr); 1441 wakeup((caddr_t)msqptr); 1442 goto msgrcvout; 1443 } 1444 user_msgp = user_msgp + tlen; /* ptr math */ 1445 next = msgmaps[next].next; 1446 } 1447 1448 /* 1449 * Done, return the actual number of bytes copied out. 1450 */ 1451 1452 msg_freehdr(msghdr); 1453 wakeup((caddr_t)msqptr); 1454 *retval = msgsz; 1455 eval = 0; 1456msgrcvout: 1457 SYSV_MSG_SUBSYS_UNLOCK(); 1458 return(eval); 1459} 1460 1461static int 1462IPCS_msg_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, 1463 __unused int arg2, struct sysctl_req *req) 1464{ 1465 int error; 1466 int cursor; 1467 union { 1468 struct user32_IPCS_command u32; 1469 struct user_IPCS_command u64; 1470 } ipcs; 1471 struct user32_msqid_ds msqid_ds32; /* post conversion, 32 bit version */ 1472 struct user64_msqid_ds msqid_ds64; /* post conversion, 64 bit version */ 1473 void *msqid_dsp; 1474 size_t ipcs_sz; 1475 size_t msqid_ds_sz; 1476 struct proc *p = current_proc(); 1477 1478 if (IS_64BIT_PROCESS(p)) { 1479 ipcs_sz = sizeof(struct user_IPCS_command); 1480 msqid_ds_sz = sizeof(struct user64_msqid_ds); 1481 } else { 1482 ipcs_sz = sizeof(struct user32_IPCS_command); 1483 msqid_ds_sz = sizeof(struct user32_msqid_ds); 1484 } 1485 1486 /* Copy in the command structure */ 1487 if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) { 1488 return(error); 1489 } 1490 1491 if (!IS_64BIT_PROCESS(p)) /* convert in place */ 1492 ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data); 1493 1494 /* Let us version this interface... */ 1495 if (ipcs.u64.ipcs_magic != IPCS_MAGIC) { 1496 return(EINVAL); 1497 } 1498 1499 SYSV_MSG_SUBSYS_LOCK(); 1500 1501 switch(ipcs.u64.ipcs_op) { 1502 case IPCS_MSG_CONF: /* Obtain global configuration data */ 1503 if (ipcs.u64.ipcs_datalen != sizeof(struct msginfo)) { 1504 error = ERANGE; 1505 break; 1506 } 1507 if (ipcs.u64.ipcs_cursor != 0) { /* fwd. compat. */ 1508 error = EINVAL; 1509 break; 1510 } 1511 SYSV_MSG_SUBSYS_UNLOCK(); 1512 error = copyout(&msginfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen); 1513 SYSV_MSG_SUBSYS_LOCK(); 1514 break; 1515 1516 case IPCS_MSG_ITER: /* Iterate over existing segments */ 1517 /* Not done up top so we can set limits via sysctl (later) */ 1518 if (!msginit(0)) { 1519 error = ENOMEM; 1520 break; 1521 } 1522 1523 cursor = ipcs.u64.ipcs_cursor; 1524 if (cursor < 0 || cursor >= msginfo.msgmni) { 1525 error = ERANGE; 1526 break; 1527 } 1528 if (ipcs.u64.ipcs_datalen != (int)msqid_ds_sz) { 1529 error = EINVAL; 1530 break; 1531 } 1532 for( ; cursor < msginfo.msgmni; cursor++) { 1533 if (msqids[cursor].u.msg_qbytes != 0) /* allocated */ 1534 break; 1535 continue; 1536 } 1537 if (cursor == msginfo.msgmni) { 1538 error = ENOENT; 1539 break; 1540 } 1541 1542 msqid_dsp = &msqids[cursor]; /* default: 64 bit */ 1543 1544 /* 1545 * If necessary, convert the 64 bit kernel segment 1546 * descriptor to a 32 bit user one. 1547 */ 1548 if (IS_64BIT_PROCESS(p)) { 1549 msqid_ds_kerneltouser64(msqid_dsp, &msqid_ds64); 1550 msqid_dsp = &msqid_ds64; 1551 } else { 1552 msqid_ds_kerneltouser32(msqid_dsp, &msqid_ds32); 1553 msqid_dsp = &msqid_ds32; 1554 } 1555 1556 SYSV_MSG_SUBSYS_UNLOCK(); 1557 error = copyout(msqid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen); 1558 if (!error) { 1559 /* update cursor */ 1560 ipcs.u64.ipcs_cursor = cursor + 1; 1561 1562 if (!IS_64BIT_PROCESS(p)) /* convert in place */ 1563 ipcs.u32.ipcs_data = CAST_DOWN_EXPLICIT(user32_addr_t,ipcs.u64.ipcs_data); 1564 error = SYSCTL_OUT(req, &ipcs, ipcs_sz); 1565 } 1566 SYSV_MSG_SUBSYS_LOCK(); 1567 break; 1568 1569 default: 1570 error = EINVAL; 1571 break; 1572 } 1573 1574 SYSV_MSG_SUBSYS_UNLOCK(); 1575 return(error); 1576} 1577 1578SYSCTL_DECL(_kern_sysv_ipcs); 1579SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, msg, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED, 1580 0, 0, IPCS_msg_sysctl, 1581 "S,IPCS_msg_command", 1582 "ipcs msg command interface"); 1583 1584#endif /* SYSV_MSG */ 1585