1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* 34 * Inter-Process Communication Message Facility. 35 * 36 * See os/ipc.c for a description of common IPC functionality. 37 * 38 * Resource controls 39 * ----------------- 40 * 41 * Control: project.max-msg-ids (rc_project_msgmni) 42 * Description: Maximum number of message queue ids allowed a project. 43 * 44 * When msgget() is used to allocate a message queue, one id is 45 * allocated. If the id allocation doesn't succeed, msgget() fails 46 * and errno is set to ENOSPC. Upon successful msgctl(, IPC_RMID) 47 * the id is deallocated. 48 * 49 * Control: process.max-msg-qbytes (rc_process_msgmnb) 50 * Description: Maximum number of bytes of messages on a message queue. 51 * 52 * When msgget() successfully allocates a message queue, the minimum 53 * enforced value of this limit is used to initialize msg_qbytes. 54 * 55 * Control: process.max-msg-messages (rc_process_msgtql) 56 * Description: Maximum number of messages on a message queue. 57 * 58 * When msgget() successfully allocates a message queue, the minimum 59 * enforced value of this limit is used to initialize a per-queue 60 * limit on the number of messages. 61 */ 62 63 #include <sys/types.h> 64 #include <sys/t_lock.h> 65 #include <sys/param.h> 66 #include <sys/cred.h> 67 #include <sys/user.h> 68 #include <sys/proc.h> 69 #include <sys/time.h> 70 #include <sys/ipc.h> 71 #include <sys/ipc_impl.h> 72 #include <sys/msg.h> 73 #include <sys/msg_impl.h> 74 #include <sys/list.h> 75 #include <sys/systm.h> 76 #include <sys/sysmacros.h> 77 #include <sys/cpuvar.h> 78 #include <sys/kmem.h> 79 #include <sys/ddi.h> 80 #include <sys/errno.h> 81 #include <sys/cmn_err.h> 82 #include <sys/debug.h> 83 #include <sys/project.h> 84 #include <sys/modctl.h> 85 #include <sys/syscall.h> 86 #include <sys/policy.h> 87 #include <sys/zone.h> 88 89 #include <c2/audit.h> 90 91 /* 92 * The following tunables are obsolete. Though for compatibility we 93 * still read and interpret msginfo_msgmnb, msginfo_msgmni, and 94 * msginfo_msgtql (see os/project.c and os/rctl_proc.c), the preferred 95 * mechanism for administrating the IPC Message facility is through the 96 * resource controls described at the top of this file. 97 */ 98 size_t msginfo_msgmax = 2048; /* (obsolete) */ 99 size_t msginfo_msgmnb = 4096; /* (obsolete) */ 100 int msginfo_msgmni = 50; /* (obsolete) */ 101 int msginfo_msgtql = 40; /* (obsolete) */ 102 int msginfo_msgssz = 8; /* (obsolete) */ 103 int msginfo_msgmap = 0; /* (obsolete) */ 104 ushort_t msginfo_msgseg = 1024; /* (obsolete) */ 105 106 extern rctl_hndl_t rc_project_msgmni; 107 extern rctl_hndl_t rc_process_msgmnb; 108 extern rctl_hndl_t rc_process_msgtql; 109 static ipc_service_t *msq_svc; 110 static zone_key_t msg_zone_key; 111 112 static void msg_dtor(kipc_perm_t *); 113 static void msg_rmid(kipc_perm_t *); 114 static void msg_remove_zone(zoneid_t, void *); 115 116 /* 117 * Module linkage information for the kernel. 118 */ 119 static ssize_t msgsys(int opcode, uintptr_t a0, uintptr_t a1, uintptr_t a2, 120 uintptr_t a4, uintptr_t a5); 121 122 static struct sysent ipcmsg_sysent = { 123 6, 124 #ifdef _LP64 125 SE_ARGC | SE_NOUNLOAD | SE_64RVAL, 126 #else 127 SE_ARGC | SE_NOUNLOAD | SE_32RVAL1, 128 #endif 129 (int (*)())msgsys 130 }; 131 132 #ifdef _SYSCALL32_IMPL 133 static ssize32_t msgsys32(int opcode, uint32_t a0, uint32_t a1, uint32_t a2, 134 uint32_t a4, uint32_t a5); 135 136 static struct sysent ipcmsg_sysent32 = { 137 6, 138 SE_ARGC | SE_NOUNLOAD | SE_32RVAL1, 139 (int (*)())msgsys32 140 }; 141 #endif /* _SYSCALL32_IMPL */ 142 143 static struct modlsys modlsys = { 144 &mod_syscallops, "System V message facility", &ipcmsg_sysent 145 }; 146 147 #ifdef _SYSCALL32_IMPL 148 static struct modlsys modlsys32 = { 149 &mod_syscallops32, "32-bit System V message facility", &ipcmsg_sysent32 150 }; 151 #endif 152 153 static struct modlinkage modlinkage = { 154 MODREV_1, 155 &modlsys, 156 #ifdef _SYSCALL32_IMPL 157 &modlsys32, 158 #endif 159 NULL 160 }; 161 162 163 int 164 _init(void) 165 { 166 int result; 167 168 msq_svc = ipcs_create("msqids", rc_project_msgmni, sizeof (kmsqid_t), 169 msg_dtor, msg_rmid, AT_IPC_MSG, 170 offsetof(kproject_data_t, kpd_msgmni)); 171 zone_key_create(&msg_zone_key, NULL, msg_remove_zone, NULL); 172 173 if ((result = mod_install(&modlinkage)) == 0) 174 return (0); 175 176 (void) zone_key_delete(msg_zone_key); 177 ipcs_destroy(msq_svc); 178 179 return (result); 180 } 181 182 int 183 _fini(void) 184 { 185 return (EBUSY); 186 } 187 188 int 189 _info(struct modinfo *modinfop) 190 { 191 return (mod_info(&modlinkage, modinfop)); 192 } 193 194 static void 195 msg_dtor(kipc_perm_t *perm) 196 { 197 kmsqid_t *qp = (kmsqid_t *)perm; 198 199 ASSERT(qp->msg_rcv_cnt == 0); 200 ASSERT(qp->msg_snd_cnt == 0); 201 ASSERT(qp->msg_cbytes == 0); 202 list_destroy(&qp->msg_list); 203 } 204 205 206 #define msg_hold(mp) (mp)->msg_copycnt++ 207 208 /* 209 * msg_rele - decrement the reference count on the message. When count 210 * reaches zero, free message header and contents. 211 */ 212 static void 213 msg_rele(struct msg *mp) 214 { 215 ASSERT(mp->msg_copycnt > 0); 216 if (mp->msg_copycnt-- == 1) { 217 if (mp->msg_addr) 218 kmem_free(mp->msg_addr, mp->msg_size); 219 kmem_free(mp, sizeof (struct msg)); 220 } 221 } 222 223 /* 224 * msgunlink - Unlink msg from queue, decrement byte count and wake up anyone 225 * waiting for free bytes on queue. 226 * 227 * Called with queue locked. 228 */ 229 static void 230 msgunlink(kmsqid_t *qp, struct msg *mp) 231 { 232 list_remove(&qp->msg_list, mp); 233 qp->msg_qnum--; 234 qp->msg_cbytes -= mp->msg_size; 235 msg_rele(mp); 236 237 /* Wake up waiting writers */ 238 if (qp->msg_snd_cnt) 239 cv_broadcast(&qp->msg_snd_cv); 240 } 241 242 static void 243 msg_rmid(kipc_perm_t *perm) 244 { 245 kmsqid_t *qp = (kmsqid_t *)perm; 246 struct msg *mp; 247 248 249 while ((mp = list_head(&qp->msg_list)) != NULL) 250 msgunlink(qp, mp); 251 ASSERT(qp->msg_cbytes == 0); 252 253 if (qp->msg_rcv_cnt) 254 cv_broadcast(&qp->msg_rcv_cv); 255 if (qp->msg_snd_cnt) 256 cv_broadcast(&qp->msg_snd_cv); 257 } 258 259 /* 260 * msgctl system call. 261 * 262 * gets q lock (via ipc_lookup), releases before return. 263 * may call users of msg_lock 264 */ 265 static int 266 msgctl(int msgid, int cmd, void *arg) 267 { 268 STRUCT_DECL(msqid_ds, ds); /* SVR4 queue work area */ 269 kmsqid_t *qp; /* ptr to associated q */ 270 int error; 271 struct cred *cr; 272 model_t mdl = get_udatamodel(); 273 struct msqid_ds64 ds64; 274 kmutex_t *lock; 275 proc_t *pp = curproc; 276 277 STRUCT_INIT(ds, mdl); 278 cr = CRED(); 279 280 /* 281 * Perform pre- or non-lookup actions (e.g. copyins, RMID). 282 */ 283 switch (cmd) { 284 case IPC_SET: 285 if (copyin(arg, STRUCT_BUF(ds), STRUCT_SIZE(ds))) 286 return (set_errno(EFAULT)); 287 break; 288 289 case IPC_SET64: 290 if (copyin(arg, &ds64, sizeof (struct msqid_ds64))) 291 return (set_errno(EFAULT)); 292 break; 293 294 case IPC_RMID: 295 if (error = ipc_rmid(msq_svc, msgid, cr)) 296 return (set_errno(error)); 297 return (0); 298 } 299 300 /* 301 * get msqid_ds for this msgid 302 */ 303 if ((lock = ipc_lookup(msq_svc, msgid, (kipc_perm_t **)&qp)) == NULL) 304 return (set_errno(EINVAL)); 305 306 switch (cmd) { 307 case IPC_SET: 308 if (STRUCT_FGET(ds, msg_qbytes) > qp->msg_qbytes && 309 secpolicy_ipc_config(cr) != 0) { 310 mutex_exit(lock); 311 return (set_errno(EPERM)); 312 } 313 if (error = ipcperm_set(msq_svc, cr, &qp->msg_perm, 314 &STRUCT_BUF(ds)->msg_perm, mdl)) { 315 mutex_exit(lock); 316 return (set_errno(error)); 317 } 318 qp->msg_qbytes = STRUCT_FGET(ds, msg_qbytes); 319 qp->msg_ctime = gethrestime_sec(); 320 break; 321 322 case IPC_STAT: 323 if (error = ipcperm_access(&qp->msg_perm, MSG_R, cr)) { 324 mutex_exit(lock); 325 return (set_errno(error)); 326 } 327 328 if (qp->msg_rcv_cnt) 329 qp->msg_perm.ipc_mode |= MSG_RWAIT; 330 if (qp->msg_snd_cnt) 331 qp->msg_perm.ipc_mode |= MSG_WWAIT; 332 ipcperm_stat(&STRUCT_BUF(ds)->msg_perm, &qp->msg_perm, mdl); 333 qp->msg_perm.ipc_mode &= ~(MSG_RWAIT|MSG_WWAIT); 334 STRUCT_FSETP(ds, msg_first, NULL); /* kernel addr */ 335 STRUCT_FSETP(ds, msg_last, NULL); 336 STRUCT_FSET(ds, msg_cbytes, qp->msg_cbytes); 337 STRUCT_FSET(ds, msg_qnum, qp->msg_qnum); 338 STRUCT_FSET(ds, msg_qbytes, qp->msg_qbytes); 339 STRUCT_FSET(ds, msg_lspid, qp->msg_lspid); 340 STRUCT_FSET(ds, msg_lrpid, qp->msg_lrpid); 341 STRUCT_FSET(ds, msg_stime, qp->msg_stime); 342 STRUCT_FSET(ds, msg_rtime, qp->msg_rtime); 343 STRUCT_FSET(ds, msg_ctime, qp->msg_ctime); 344 break; 345 346 case IPC_SET64: 347 mutex_enter(&pp->p_lock); 348 if ((ds64.msgx_qbytes > qp->msg_qbytes) && 349 secpolicy_ipc_config(cr) != 0 && 350 rctl_test(rc_process_msgmnb, pp->p_rctls, pp, 351 ds64.msgx_qbytes, RCA_SAFE) & RCT_DENY) { 352 mutex_exit(&pp->p_lock); 353 mutex_exit(lock); 354 return (set_errno(EPERM)); 355 } 356 mutex_exit(&pp->p_lock); 357 if (error = ipcperm_set64(msq_svc, cr, &qp->msg_perm, 358 &ds64.msgx_perm)) { 359 mutex_exit(lock); 360 return (set_errno(error)); 361 } 362 qp->msg_qbytes = ds64.msgx_qbytes; 363 qp->msg_ctime = gethrestime_sec(); 364 break; 365 366 case IPC_STAT64: 367 if (qp->msg_rcv_cnt) 368 qp->msg_perm.ipc_mode |= MSG_RWAIT; 369 if (qp->msg_snd_cnt) 370 qp->msg_perm.ipc_mode |= MSG_WWAIT; 371 ipcperm_stat64(&ds64.msgx_perm, &qp->msg_perm); 372 qp->msg_perm.ipc_mode &= ~(MSG_RWAIT|MSG_WWAIT); 373 ds64.msgx_cbytes = qp->msg_cbytes; 374 ds64.msgx_qnum = qp->msg_qnum; 375 ds64.msgx_qbytes = qp->msg_qbytes; 376 ds64.msgx_lspid = qp->msg_lspid; 377 ds64.msgx_lrpid = qp->msg_lrpid; 378 ds64.msgx_stime = qp->msg_stime; 379 ds64.msgx_rtime = qp->msg_rtime; 380 ds64.msgx_ctime = qp->msg_ctime; 381 break; 382 383 default: 384 mutex_exit(lock); 385 return (set_errno(EINVAL)); 386 } 387 388 mutex_exit(lock); 389 390 /* 391 * Do copyout last (after releasing mutex). 392 */ 393 switch (cmd) { 394 case IPC_STAT: 395 if (copyout(STRUCT_BUF(ds), arg, STRUCT_SIZE(ds))) 396 return (set_errno(EFAULT)); 397 break; 398 399 case IPC_STAT64: 400 if (copyout(&ds64, arg, sizeof (struct msqid_ds64))) 401 return (set_errno(EFAULT)); 402 break; 403 } 404 405 return (0); 406 } 407 408 /* 409 * Remove all message queues associated with a given zone. Called by 410 * zone_shutdown when the zone is halted. 411 */ 412 /*ARGSUSED1*/ 413 static void 414 msg_remove_zone(zoneid_t zoneid, void *arg) 415 { 416 ipc_remove_zone(msq_svc, zoneid); 417 } 418 419 /* 420 * msgget system call. 421 */ 422 static int 423 msgget(key_t key, int msgflg) 424 { 425 kmsqid_t *qp; 426 kmutex_t *lock; 427 int id, error; 428 proc_t *pp = curproc; 429 430 top: 431 if (error = ipc_get(msq_svc, key, msgflg, (kipc_perm_t **)&qp, &lock)) 432 return (set_errno(error)); 433 434 if (IPC_FREE(&qp->msg_perm)) { 435 mutex_exit(lock); 436 mutex_exit(&pp->p_lock); 437 438 list_create(&qp->msg_list, sizeof (struct msg), 439 offsetof(struct msg, msg_node)); 440 qp->msg_qnum = 0; 441 qp->msg_lspid = qp->msg_lrpid = 0; 442 qp->msg_stime = qp->msg_rtime = 0; 443 qp->msg_ctime = gethrestime_sec(); 444 qp->msg_rcv_cnt = qp->msg_snd_cnt = 0; 445 446 if (error = ipc_commit_begin(msq_svc, key, msgflg, 447 (kipc_perm_t *)qp)) { 448 if (error == EAGAIN) 449 goto top; 450 return (set_errno(error)); 451 } 452 qp->msg_qbytes = rctl_enforced_value(rc_process_msgmnb, 453 pp->p_rctls, pp); 454 qp->msg_qmax = rctl_enforced_value(rc_process_msgtql, 455 pp->p_rctls, pp); 456 lock = ipc_commit_end(msq_svc, &qp->msg_perm); 457 } 458 #ifdef C2_AUDIT 459 if (audit_active) 460 audit_ipcget(AT_IPC_MSG, (void *)qp); 461 #endif 462 id = qp->msg_perm.ipc_id; 463 mutex_exit(lock); 464 return (id); 465 } 466 467 /* 468 * msgrcv system call. 469 */ 470 static ssize_t 471 msgrcv(int msqid, struct ipcmsgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) 472 { 473 struct msg *mp; /* ptr to msg on q */ 474 struct msg *smp; /* ptr to best msg on q */ 475 kmsqid_t *qp; /* ptr to associated q */ 476 kmutex_t *lock; 477 size_t xtsz; /* transfer byte count */ 478 int error = 0, copyerror = 0; 479 int cvres; 480 STRUCT_HANDLE(ipcmsgbuf, umsgp); 481 model_t mdl = get_udatamodel(); 482 483 CPU_STATS_ADDQ(CPU, sys, msg, 1); /* bump msg send/rcv count */ 484 STRUCT_SET_HANDLE(umsgp, mdl, msgp); 485 486 if ((lock = ipc_lookup(msq_svc, msqid, (kipc_perm_t **)&qp)) == NULL) 487 return ((ssize_t)set_errno(EINVAL)); 488 ipc_hold(msq_svc, (kipc_perm_t *)qp); 489 490 if (error = ipcperm_access(&qp->msg_perm, MSG_R, CRED())) 491 goto msgrcv_out; 492 493 findmsg: 494 smp = NULL; 495 mp = list_head(&qp->msg_list); 496 if (msgtyp == 0) { 497 smp = mp; 498 } else { 499 for (; mp; mp = list_next(&qp->msg_list, mp)) { 500 if (msgtyp > 0) { 501 if (msgtyp != mp->msg_type) 502 continue; 503 smp = mp; 504 break; 505 } 506 if (mp->msg_type <= -msgtyp) { 507 if (smp && smp->msg_type <= mp->msg_type) 508 continue; 509 smp = mp; 510 } 511 } 512 } 513 514 if (smp) { 515 /* 516 * Message found. 517 */ 518 if ((smp->msg_flags & MSG_RCVCOPY) == 0) { 519 /* 520 * No one else is copying this message. Copy it. 521 */ 522 if (msgsz < smp->msg_size) { 523 if ((msgflg & MSG_NOERROR) == 0) { 524 error = E2BIG; 525 goto msgrcv_out; 526 } else { 527 xtsz = msgsz; 528 } 529 } else { 530 xtsz = smp->msg_size; 531 } 532 533 /* 534 * Mark message as being copied out. Release mutex 535 * while copying out. 536 */ 537 ASSERT((smp->msg_flags & MSG_RCVCOPY) == 0); 538 smp->msg_flags |= MSG_RCVCOPY; 539 msg_hold(smp); 540 mutex_exit(lock); 541 542 if (mdl == DATAMODEL_NATIVE) { 543 copyerror = copyout(&smp->msg_type, msgp, 544 sizeof (smp->msg_type)); 545 } else { 546 /* 547 * 32-bit callers need an imploded msg type. 548 */ 549 int32_t msg_type32 = smp->msg_type; 550 551 copyerror = copyout(&msg_type32, msgp, 552 sizeof (msg_type32)); 553 } 554 555 if (copyerror == 0 && xtsz) 556 copyerror = copyout(smp->msg_addr, 557 STRUCT_FADDR(umsgp, mtext), xtsz); 558 559 /* 560 * Reclaim mutex, make sure queue still exists, 561 * and remove message. 562 */ 563 lock = ipc_lock(msq_svc, qp->msg_perm.ipc_id); 564 ASSERT(smp->msg_flags & MSG_RCVCOPY); 565 smp->msg_flags &= ~MSG_RCVCOPY; 566 msg_rele(smp); 567 568 if (IPC_FREE(&qp->msg_perm)) { 569 error = EIDRM; 570 goto msgrcv_out; 571 } 572 cv_broadcast(&qp->msg_rcv_cv); 573 574 if (copyerror) { 575 error = EFAULT; 576 goto msgrcv_out; 577 } 578 qp->msg_lrpid = ttoproc(curthread)->p_pid; 579 qp->msg_rtime = gethrestime_sec(); 580 msgunlink(qp, smp); 581 goto msgrcv_out; 582 } 583 584 } else { 585 /* 586 * No message found. 587 */ 588 if (msgflg & IPC_NOWAIT) { 589 error = ENOMSG; 590 goto msgrcv_out; 591 } 592 } 593 594 /* Wait for new message */ 595 qp->msg_rcv_cnt++; 596 cvres = cv_wait_sig(&qp->msg_rcv_cv, lock); 597 lock = ipc_relock(msq_svc, qp->msg_perm.ipc_id, lock); 598 qp->msg_rcv_cnt--; 599 600 if (IPC_FREE(&qp->msg_perm)) { 601 error = EIDRM; 602 goto msgrcv_out; 603 } 604 if (cvres == 0) { 605 error = EINTR; 606 goto msgrcv_out; 607 } 608 609 goto findmsg; 610 611 msgrcv_out: 612 ipc_rele(msq_svc, (kipc_perm_t *)qp); 613 if (error) 614 return ((ssize_t)set_errno(error)); 615 return ((ssize_t)xtsz); 616 } 617 618 /* 619 * msgids system call. 620 */ 621 static int 622 msgids(int *buf, uint_t nids, uint_t *pnids) 623 { 624 int error; 625 626 if (error = ipc_ids(msq_svc, buf, nids, pnids)) 627 return (set_errno(error)); 628 629 return (0); 630 } 631 632 #define RND(x) roundup((x), sizeof (size_t)) 633 #define RND32(x) roundup((x), sizeof (size32_t)) 634 635 /* 636 * msgsnap system call. 637 */ 638 static int 639 msgsnap(int msqid, caddr_t buf, size_t bufsz, long msgtyp) 640 { 641 struct msg *mp; /* ptr to msg on q */ 642 kmsqid_t *qp; /* ptr to associated q */ 643 kmutex_t *lock; 644 size_t size; 645 size_t nmsg; 646 struct msg **snaplist; 647 int error, i; 648 model_t mdl = get_udatamodel(); 649 STRUCT_DECL(msgsnap_head, head); 650 STRUCT_DECL(msgsnap_mhead, mhead); 651 652 STRUCT_INIT(head, mdl); 653 STRUCT_INIT(mhead, mdl); 654 655 if (bufsz < STRUCT_SIZE(head)) 656 return (set_errno(EINVAL)); 657 658 if ((lock = ipc_lookup(msq_svc, msqid, (kipc_perm_t **)&qp)) == NULL) 659 return (set_errno(EINVAL)); 660 661 if (error = ipcperm_access(&qp->msg_perm, MSG_R, CRED())) { 662 mutex_exit(lock); 663 return (set_errno(error)); 664 } 665 ipc_hold(msq_svc, (kipc_perm_t *)qp); 666 667 /* 668 * First compute the required buffer size and 669 * the number of messages on the queue. 670 */ 671 size = nmsg = 0; 672 for (mp = list_head(&qp->msg_list); mp; 673 mp = list_next(&qp->msg_list, mp)) { 674 if (msgtyp == 0 || 675 (msgtyp > 0 && msgtyp == mp->msg_type) || 676 (msgtyp < 0 && mp->msg_type <= -msgtyp)) { 677 nmsg++; 678 if (mdl == DATAMODEL_NATIVE) 679 size += RND(mp->msg_size); 680 else 681 size += RND32(mp->msg_size); 682 } 683 } 684 685 size += STRUCT_SIZE(head) + nmsg * STRUCT_SIZE(mhead); 686 if (size > bufsz) 687 nmsg = 0; 688 689 if (nmsg > 0) { 690 /* 691 * Mark the messages as being copied. 692 */ 693 snaplist = (struct msg **)kmem_alloc(nmsg * 694 sizeof (struct msg *), KM_SLEEP); 695 i = 0; 696 for (mp = list_head(&qp->msg_list); mp; 697 mp = list_next(&qp->msg_list, mp)) { 698 if (msgtyp == 0 || 699 (msgtyp > 0 && msgtyp == mp->msg_type) || 700 (msgtyp < 0 && mp->msg_type <= -msgtyp)) { 701 msg_hold(mp); 702 snaplist[i] = mp; 703 i++; 704 } 705 } 706 } 707 mutex_exit(lock); 708 709 /* 710 * Copy out the buffer header. 711 */ 712 STRUCT_FSET(head, msgsnap_size, size); 713 STRUCT_FSET(head, msgsnap_nmsg, nmsg); 714 if (copyout(STRUCT_BUF(head), buf, STRUCT_SIZE(head))) 715 error = EFAULT; 716 717 buf += STRUCT_SIZE(head); 718 719 /* 720 * Now copy out the messages one by one. 721 */ 722 for (i = 0; i < nmsg; i++) { 723 mp = snaplist[i]; 724 if (error == 0) { 725 STRUCT_FSET(mhead, msgsnap_mlen, mp->msg_size); 726 STRUCT_FSET(mhead, msgsnap_mtype, mp->msg_type); 727 if (copyout(STRUCT_BUF(mhead), buf, STRUCT_SIZE(mhead))) 728 error = EFAULT; 729 buf += STRUCT_SIZE(mhead); 730 731 if (error == 0 && 732 mp->msg_size != 0 && 733 copyout(mp->msg_addr, buf, mp->msg_size)) 734 error = EFAULT; 735 if (mdl == DATAMODEL_NATIVE) 736 buf += RND(mp->msg_size); 737 else 738 buf += RND32(mp->msg_size); 739 } 740 lock = ipc_lock(msq_svc, qp->msg_perm.ipc_id); 741 msg_rele(mp); 742 /* Check for msg q deleted or reallocated */ 743 if (IPC_FREE(&qp->msg_perm)) 744 error = EIDRM; 745 mutex_exit(lock); 746 } 747 748 (void) ipc_lock(msq_svc, qp->msg_perm.ipc_id); 749 ipc_rele(msq_svc, (kipc_perm_t *)qp); 750 751 if (nmsg > 0) 752 kmem_free(snaplist, nmsg * sizeof (struct msg *)); 753 754 if (error) 755 return (set_errno(error)); 756 return (0); 757 } 758 759 /* 760 * msgsnd system call. 761 */ 762 static int 763 msgsnd(int msqid, struct ipcmsgbuf *msgp, size_t msgsz, int msgflg) 764 { 765 kmsqid_t *qp; 766 kmutex_t *lock; 767 struct msg *mp = NULL; 768 long type; 769 int error = 0; 770 model_t mdl = get_udatamodel(); 771 STRUCT_HANDLE(ipcmsgbuf, umsgp); 772 773 CPU_STATS_ADDQ(CPU, sys, msg, 1); /* bump msg send/rcv count */ 774 STRUCT_SET_HANDLE(umsgp, mdl, msgp); 775 776 if (mdl == DATAMODEL_NATIVE) { 777 if (copyin(msgp, &type, sizeof (type))) 778 return (set_errno(EFAULT)); 779 } else { 780 int32_t type32; 781 if (copyin(msgp, &type32, sizeof (type32))) 782 return (set_errno(EFAULT)); 783 type = type32; 784 } 785 786 if (type < 1) 787 return (set_errno(EINVAL)); 788 789 if ((lock = ipc_lookup(msq_svc, msqid, (kipc_perm_t **)&qp)) == NULL) 790 return (set_errno(EINVAL)); 791 ipc_hold(msq_svc, (kipc_perm_t *)qp); 792 793 if (msgsz > qp->msg_qbytes) { 794 error = EINVAL; 795 goto msgsnd_out; 796 } 797 798 if (error = ipcperm_access(&qp->msg_perm, MSG_W, CRED())) 799 goto msgsnd_out; 800 801 top: 802 /* 803 * Allocate space on q, message header, & buffer space. 804 */ 805 ASSERT(qp->msg_qnum <= qp->msg_qmax); 806 while ((msgsz > qp->msg_qbytes - qp->msg_cbytes) || 807 (qp->msg_qnum == qp->msg_qmax)) { 808 int cvres; 809 810 if (msgflg & IPC_NOWAIT) { 811 error = EAGAIN; 812 goto msgsnd_out; 813 } 814 815 qp->msg_snd_cnt++; 816 cvres = cv_wait_sig(&qp->msg_snd_cv, lock); 817 lock = ipc_relock(msq_svc, qp->msg_perm.ipc_id, lock); 818 qp->msg_snd_cnt--; 819 820 if (IPC_FREE(&qp->msg_perm)) { 821 error = EIDRM; 822 goto msgsnd_out; 823 } 824 825 if (cvres == 0) { 826 error = EINTR; 827 goto msgsnd_out; 828 } 829 } 830 831 if (mp == NULL) { 832 int failure; 833 834 mutex_exit(lock); 835 mp = kmem_zalloc(sizeof (struct msg), KM_SLEEP); 836 mp->msg_addr = kmem_zalloc(msgsz, KM_SLEEP); 837 mp->msg_size = msgsz; 838 mp->msg_copycnt = 1; 839 840 failure = msgsz && (copyin(STRUCT_FADDR(umsgp, mtext), 841 mp->msg_addr, msgsz) == -1); 842 lock = ipc_lock(msq_svc, qp->msg_perm.ipc_id); 843 if (IPC_FREE(&qp->msg_perm)) { 844 error = EIDRM; 845 goto msgsnd_out; 846 } 847 if (failure) { 848 error = EFAULT; 849 goto msgsnd_out; 850 } 851 goto top; 852 } 853 854 /* 855 * Everything is available, put msg on q. 856 */ 857 qp->msg_qnum++; 858 qp->msg_cbytes += msgsz; 859 qp->msg_lspid = curproc->p_pid; 860 qp->msg_stime = gethrestime_sec(); 861 mp->msg_type = type; 862 mp->msg_flags = 0; 863 list_insert_tail(&qp->msg_list, mp); 864 if (qp->msg_rcv_cnt) 865 cv_broadcast(&qp->msg_rcv_cv); 866 867 msgsnd_out: 868 ipc_rele(msq_svc, (kipc_perm_t *)qp); /* drops lock */ 869 870 if (error) { 871 if (mp) 872 msg_rele(mp); 873 return (set_errno(error)); 874 } 875 876 return (0); 877 } 878 879 /* 880 * msgsys - System entry point for msgctl, msgget, msgrcv, and msgsnd 881 * system calls. 882 */ 883 static ssize_t 884 msgsys(int opcode, uintptr_t a1, uintptr_t a2, uintptr_t a3, 885 uintptr_t a4, uintptr_t a5) 886 { 887 ssize_t error; 888 889 switch (opcode) { 890 case MSGGET: 891 error = msgget((key_t)a1, (int)a2); 892 break; 893 case MSGCTL: 894 error = msgctl((int)a1, (int)a2, (void *)a3); 895 break; 896 case MSGRCV: 897 error = msgrcv((int)a1, (struct ipcmsgbuf *)a2, 898 (size_t)a3, (long)a4, (int)a5); 899 break; 900 case MSGSND: 901 error = msgsnd((int)a1, (struct ipcmsgbuf *)a2, 902 (size_t)a3, (int)a4); 903 break; 904 case MSGIDS: 905 error = msgids((int *)a1, (uint_t)a2, (uint_t *)a3); 906 break; 907 case MSGSNAP: 908 error = msgsnap((int)a1, (caddr_t)a2, (size_t)a3, (long)a4); 909 break; 910 default: 911 error = set_errno(EINVAL); 912 break; 913 } 914 915 return (error); 916 } 917 918 #ifdef _SYSCALL32_IMPL 919 /* 920 * msgsys32 - System entry point for msgctl, msgget, msgrcv, and msgsnd 921 * system calls for 32-bit callers on LP64 kernel. 922 */ 923 static ssize32_t 924 msgsys32(int opcode, uint32_t a1, uint32_t a2, uint32_t a3, 925 uint32_t a4, uint32_t a5) 926 { 927 ssize_t error; 928 929 switch (opcode) { 930 case MSGGET: 931 error = msgget((key_t)a1, (int)a2); 932 break; 933 case MSGCTL: 934 error = msgctl((int)a1, (int)a2, (void *)(uintptr_t)a3); 935 break; 936 case MSGRCV: 937 error = msgrcv((int)a1, (struct ipcmsgbuf *)(uintptr_t)a2, 938 (size_t)a3, (long)(int32_t)a4, (int)a5); 939 break; 940 case MSGSND: 941 error = msgsnd((int)a1, (struct ipcmsgbuf *)(uintptr_t)a2, 942 (size_t)(int32_t)a3, (int)a4); 943 break; 944 case MSGIDS: 945 error = msgids((int *)(uintptr_t)a1, (uint_t)a2, 946 (uint_t *)(uintptr_t)a3); 947 break; 948 case MSGSNAP: 949 error = msgsnap((int)a1, (caddr_t)(uintptr_t)a2, (size_t)a3, 950 (long)(int32_t)a4); 951 break; 952 default: 953 error = set_errno(EINVAL); 954 break; 955 } 956 957 return (error); 958 } 959 #endif /* SYSCALL32_IMPL */ 960