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