1 /* $OpenBSD: sysv_msg.c,v 1.34 2018/12/05 15:42:45 mpi Exp $ */ 2 /* $NetBSD: sysv_msg.c,v 1.19 1996/02/09 19:00:18 christos Exp $ */ 3 /* 4 * Copyright (c) 2009 Bret S. Lambert <blambert@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 /* 19 * Implementation of SVID messages 20 * 21 * Author: Daniel Boulet 22 * 23 * Copyright 1993 Daniel Boulet and RTMX Inc. 24 * 25 * This system call was implemented by Daniel Boulet under contract from RTMX. 26 * 27 * Redistribution and use in source forms, with and without modification, 28 * are permitted provided that this entire comment appears intact. 29 * 30 * Redistribution in binary form may occur without any restrictions. 31 * Obviously, it would be nice if you gave credit where credit is due 32 * but requiring it would be too onerous. 33 * 34 * This software is provided ``AS IS'' without any warranties of any kind. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/malloc.h> 39 #include <sys/mbuf.h> 40 #include <sys/mount.h> 41 #include <sys/msg.h> 42 #include <sys/pool.h> 43 #include <sys/proc.h> 44 #include <sys/queue.h> 45 #include <sys/syscallargs.h> 46 #include <sys/sysctl.h> 47 #include <sys/systm.h> 48 #include <sys/uio.h> 49 50 struct que *que_create(key_t, struct ucred *, int); 51 struct que *que_lookup(int); 52 struct que *que_key_lookup(key_t); 53 void que_wakewriters(void); 54 void que_free(struct que *); 55 struct msg *msg_create(struct que *); 56 void msg_free(struct msg *); 57 void msg_enqueue(struct que *, struct msg *, struct proc *); 58 void msg_dequeue(struct que *, struct msg *, struct proc *); 59 struct msg *msg_lookup(struct que *, int); 60 int msg_copyin(struct msg *, const char *, size_t, struct proc *); 61 int msg_copyout(struct msg *, char *, size_t *, struct proc *); 62 63 struct pool sysvmsgpl; 64 struct msginfo msginfo; 65 66 TAILQ_HEAD(, que) msg_queues; 67 68 int num_ques; 69 int num_msgs; 70 int sequence; 71 int maxmsgs; 72 73 void 74 msginit(void) 75 { 76 msginfo.msgmax = MSGMAX; 77 msginfo.msgmni = MSGMNI; 78 msginfo.msgmnb = MSGMNB; 79 msginfo.msgtql = MSGTQL; 80 msginfo.msgssz = MSGSSZ; 81 msginfo.msgseg = MSGSEG; 82 83 pool_init(&sysvmsgpl, sizeof(struct msg), 0, IPL_NONE, PR_WAITOK, 84 "sysvmsgpl", NULL); 85 86 TAILQ_INIT(&msg_queues); 87 88 num_ques = 0; 89 num_msgs = 0; 90 sequence = 1; 91 maxmsgs = 0; 92 } 93 94 int 95 sys_msgctl(struct proc *p, void *v, register_t *retval) 96 { 97 struct sys_msgctl_args /* { 98 syscallarg(int) msqid; 99 syscallarg(int) cmd; 100 syscallarg(struct msqid_ds *) buf; 101 } */ *uap = v; 102 103 return (msgctl1(p, SCARG(uap, msqid), SCARG(uap, cmd), 104 (caddr_t)SCARG(uap, buf), copyin, copyout)); 105 } 106 107 int 108 msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf, 109 int (*ds_copyin)(const void *, void *, size_t), 110 int (*ds_copyout)(const void *, void *, size_t)) 111 { 112 struct msqid_ds tmp; 113 struct ucred *cred = p->p_ucred; 114 struct que *que; 115 int error = 0; 116 117 if ((que = que_lookup(msqid)) == NULL) 118 return (EINVAL); 119 120 QREF(que); 121 122 switch (cmd) { 123 124 case IPC_RMID: 125 if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M))) 126 goto out; 127 128 TAILQ_REMOVE(&msg_queues, que, que_next); 129 que->que_flags |= MSGQ_DYING; 130 131 /* lose interest in the queue and wait for others to too */ 132 if (--que->que_references > 0) { 133 wakeup(que); 134 tsleep(&que->que_references, PZERO, "msgqrm", 0); 135 } 136 137 que_free(que); 138 139 return (0); 140 141 case IPC_SET: 142 if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M))) 143 goto out; 144 if ((error = ds_copyin(buf, &tmp, sizeof(struct msqid_ds)))) 145 goto out; 146 147 /* only superuser can bump max bytes in queue */ 148 if (tmp.msg_qbytes > que->msqid_ds.msg_qbytes && 149 cred->cr_uid != 0) { 150 error = EPERM; 151 goto out; 152 } 153 154 /* restrict max bytes in queue to system limit */ 155 if (tmp.msg_qbytes > msginfo.msgmnb) 156 tmp.msg_qbytes = msginfo.msgmnb; 157 158 /* can't reduce msg_bytes to 0 */ 159 if (tmp.msg_qbytes == 0) { 160 error = EINVAL; /* non-standard errno! */ 161 goto out; 162 } 163 164 que->msqid_ds.msg_perm.uid = tmp.msg_perm.uid; 165 que->msqid_ds.msg_perm.gid = tmp.msg_perm.gid; 166 que->msqid_ds.msg_perm.mode = 167 (que->msqid_ds.msg_perm.mode & ~0777) | 168 (tmp.msg_perm.mode & 0777); 169 que->msqid_ds.msg_qbytes = tmp.msg_qbytes; 170 que->msqid_ds.msg_ctime = time_second; 171 break; 172 173 case IPC_STAT: 174 if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R))) 175 goto out; 176 error = ds_copyout(&que->msqid_ds, buf, 177 sizeof(struct msqid_ds)); 178 break; 179 180 default: 181 error = EINVAL; 182 break; 183 } 184 out: 185 QRELE(que); 186 187 return (error); 188 } 189 190 int 191 sys_msgget(struct proc *p, void *v, register_t *retval) 192 { 193 struct sys_msgget_args /* { 194 syscallarg(key_t) key; 195 syscallarg(int) msgflg; 196 } */ *uap = v; 197 struct ucred *cred = p->p_ucred; 198 struct que *que; 199 key_t key = SCARG(uap, key); 200 int msgflg = SCARG(uap, msgflg); 201 int error = 0; 202 203 again: 204 if (key != IPC_PRIVATE) { 205 que = que_key_lookup(key); 206 if (que) { 207 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) 208 return (EEXIST); 209 if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, 210 msgflg & 0700))) 211 return (error); 212 goto found; 213 } 214 } 215 216 /* don't create a new message queue if the caller doesn't want to */ 217 if (key != IPC_PRIVATE && !(msgflg & IPC_CREAT)) 218 return (ENOENT); 219 220 /* enforce limits on the maximum number of message queues */ 221 if (num_ques >= msginfo.msgmni) 222 return (ENOSPC); 223 224 /* 225 * if que_create returns NULL, it means that a que with an identical 226 * key was created while this process was sleeping, so start over 227 */ 228 if ((que = que_create(key, cred, msgflg & 0777)) == NULL) 229 goto again; 230 231 found: 232 *retval = IXSEQ_TO_IPCID(que->que_ix, que->msqid_ds.msg_perm); 233 return (error); 234 } 235 236 #define MSGQ_SPACE(q) ((q)->msqid_ds.msg_qbytes - (q)->msqid_ds.msg_cbytes) 237 238 int 239 sys_msgsnd(struct proc *p, void *v, register_t *retval) 240 { 241 struct sys_msgsnd_args /* { 242 syscallarg(int) msqid; 243 syscallarg(const void *) msgp; 244 syscallarg(size_t) msgsz; 245 syscallarg(int) msgflg; 246 } */ *uap = v; 247 struct ucred *cred = p->p_ucred; 248 struct que *que; 249 struct msg *msg; 250 size_t msgsz = SCARG(uap, msgsz); 251 int error; 252 253 if ((que = que_lookup(SCARG(uap, msqid))) == NULL) 254 return (EINVAL); 255 256 if (msgsz > que->msqid_ds.msg_qbytes || msgsz > msginfo.msgmax) 257 return (EINVAL); 258 259 if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_W))) 260 return (error); 261 262 QREF(que); 263 264 while (MSGQ_SPACE(que) < msgsz || num_msgs >= msginfo.msgtql) { 265 266 if (SCARG(uap, msgflg) & IPC_NOWAIT) { 267 error = EAGAIN; 268 goto out; 269 } 270 271 /* notify world that process may wedge here */ 272 if (num_msgs >= msginfo.msgtql) 273 maxmsgs = 1; 274 275 que->que_flags |= MSGQ_WRITERS; 276 if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0))) 277 goto out; 278 279 if (que->que_flags & MSGQ_DYING) { 280 error = EIDRM; 281 goto out; 282 } 283 } 284 285 /* if msg_create returns NULL, the queue is being removed */ 286 if ((msg = msg_create(que)) == NULL) { 287 error = EIDRM; 288 goto out; 289 } 290 291 /* msg_copyin frees msg on error */ 292 if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz, p))) 293 goto out; 294 295 msg_enqueue(que, msg, p); 296 297 if (que->que_flags & MSGQ_READERS) { 298 que->que_flags &= ~MSGQ_READERS; 299 wakeup(que); 300 } 301 302 if (que->que_flags & MSGQ_DYING) { 303 error = EIDRM; 304 wakeup(que); 305 } 306 out: 307 QRELE(que); 308 309 return (error); 310 } 311 312 int 313 sys_msgrcv(struct proc *p, void *v, register_t *retval) 314 { 315 struct sys_msgrcv_args /* { 316 syscallarg(int) msqid; 317 syscallarg(void *) msgp; 318 syscallarg(size_t) msgsz; 319 syscallarg(long) msgtyp; 320 syscallarg(int) msgflg; 321 } */ *uap = v; 322 struct ucred *cred = p->p_ucred; 323 char *msgp = SCARG(uap, msgp); 324 struct que *que; 325 struct msg *msg; 326 size_t msgsz = SCARG(uap, msgsz); 327 long msgtyp = SCARG(uap, msgtyp); 328 int error; 329 330 if ((que = que_lookup(SCARG(uap, msqid))) == NULL) 331 return (EINVAL); 332 333 if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R))) 334 return (error); 335 336 QREF(que); 337 338 /* msg_lookup handles matching; sleeping gets handled here */ 339 while ((msg = msg_lookup(que, msgtyp)) == NULL) { 340 341 if (SCARG(uap, msgflg) & IPC_NOWAIT) { 342 error = ENOMSG; 343 goto out; 344 } 345 346 que->que_flags |= MSGQ_READERS; 347 if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0))) 348 goto out; 349 350 /* make sure the queue still alive */ 351 if (que->que_flags & MSGQ_DYING) { 352 error = EIDRM; 353 goto out; 354 } 355 } 356 357 /* if msg_copyout fails, keep the message around so it isn't lost */ 358 if ((error = msg_copyout(msg, msgp, &msgsz, p))) 359 goto out; 360 361 msg_dequeue(que, msg, p); 362 msg_free(msg); 363 364 if (que->que_flags & MSGQ_WRITERS) { 365 que->que_flags &= ~MSGQ_WRITERS; 366 wakeup(que); 367 } 368 369 /* ensure processes waiting on the global limit don't wedge */ 370 if (maxmsgs) { 371 maxmsgs = 0; 372 que_wakewriters(); 373 } 374 375 *retval = msgsz; 376 out: 377 QRELE(que); 378 379 return (error); 380 } 381 382 /* 383 * que management functions 384 */ 385 386 struct que * 387 que_create(key_t key, struct ucred *cred, int mode) 388 { 389 struct que *que, *que2; 390 int nextix = 1; 391 392 que = malloc(sizeof(*que), M_TEMP, M_WAIT|M_ZERO); 393 394 /* if malloc slept, a queue with the same key may have been created */ 395 if (que_key_lookup(key)) { 396 free(que, M_TEMP, sizeof *que); 397 return (NULL); 398 } 399 400 /* find next available "index" */ 401 TAILQ_FOREACH(que2, &msg_queues, que_next) { 402 if (nextix < que2->que_ix) 403 break; 404 nextix = que2->que_ix + 1; 405 } 406 que->que_ix = nextix; 407 408 que->msqid_ds.msg_perm.key = key; 409 que->msqid_ds.msg_perm.cuid = cred->cr_uid; 410 que->msqid_ds.msg_perm.uid = cred->cr_uid; 411 que->msqid_ds.msg_perm.cgid = cred->cr_gid; 412 que->msqid_ds.msg_perm.gid = cred->cr_gid; 413 que->msqid_ds.msg_perm.mode = mode & 0777; 414 que->msqid_ds.msg_perm.seq = ++sequence & 0x7fff; 415 que->msqid_ds.msg_qbytes = msginfo.msgmnb; 416 que->msqid_ds.msg_ctime = time_second; 417 418 TAILQ_INIT(&que->que_msgs); 419 420 /* keep queues in "index" order */ 421 if (que2) 422 TAILQ_INSERT_BEFORE(que2, que, que_next); 423 else 424 TAILQ_INSERT_TAIL(&msg_queues, que, que_next); 425 num_ques++; 426 427 return (que); 428 } 429 430 struct que * 431 que_lookup(int id) 432 { 433 struct que *que; 434 435 TAILQ_FOREACH(que, &msg_queues, que_next) 436 if (que->que_ix == IPCID_TO_IX(id)) 437 break; 438 439 /* don't return queues marked for removal */ 440 if (que && que->que_flags & MSGQ_DYING) 441 return (NULL); 442 443 return (que); 444 } 445 446 struct que * 447 que_key_lookup(key_t key) 448 { 449 struct que *que; 450 451 if (key == IPC_PRIVATE) 452 return (NULL); 453 454 TAILQ_FOREACH(que, &msg_queues, que_next) 455 if (que->msqid_ds.msg_perm.key == key) 456 break; 457 458 /* don't return queues marked for removal */ 459 if (que && que->que_flags & MSGQ_DYING) 460 return (NULL); 461 462 return (que); 463 } 464 465 void 466 que_wakewriters(void) 467 { 468 struct que *que; 469 470 TAILQ_FOREACH(que, &msg_queues, que_next) { 471 if (que->que_flags & MSGQ_WRITERS) { 472 que->que_flags &= ~MSGQ_WRITERS; 473 wakeup(que); 474 } 475 } 476 } 477 478 void 479 que_free(struct que *que) 480 { 481 struct msg *msg; 482 #ifdef DIAGNOSTIC 483 if (que->que_references > 0) 484 panic("freeing message queue with active references"); 485 #endif 486 487 while ((msg = TAILQ_FIRST(&que->que_msgs))) { 488 TAILQ_REMOVE(&que->que_msgs, msg, msg_next); 489 msg_free(msg); 490 } 491 free(que, M_TEMP, sizeof *que); 492 num_ques--; 493 } 494 495 /* 496 * msg management functions 497 */ 498 499 struct msg * 500 msg_create(struct que *que) 501 { 502 struct msg *msg; 503 504 msg = pool_get(&sysvmsgpl, PR_WAITOK|PR_ZERO); 505 506 /* if the queue has died during allocation, return NULL */ 507 if (que->que_flags & MSGQ_DYING) { 508 pool_put(&sysvmsgpl, msg); 509 wakeup(que); 510 return(NULL); 511 } 512 513 num_msgs++; 514 515 return (msg); 516 } 517 518 struct msg * 519 msg_lookup(struct que *que, int msgtyp) 520 { 521 struct msg *msg; 522 523 /* 524 * Three different matches are performed based on the value of msgtyp: 525 * 1) msgtyp > 0 => match exactly 526 * 2) msgtyp = 0 => match any 527 * 3) msgtyp < 0 => match any up to absolute value of msgtyp 528 */ 529 TAILQ_FOREACH(msg, &que->que_msgs, msg_next) 530 if (msgtyp == 0 || msgtyp == msg->msg_type || 531 (msgtyp < 0 && -msgtyp <= msg->msg_type)) 532 break; 533 534 return (msg); 535 } 536 537 void 538 msg_free(struct msg *msg) 539 { 540 m_freem(msg->msg_data); 541 pool_put(&sysvmsgpl, msg); 542 num_msgs--; 543 } 544 545 void 546 msg_enqueue(struct que *que, struct msg *msg, struct proc *p) 547 { 548 que->msqid_ds.msg_cbytes += msg->msg_len; 549 que->msqid_ds.msg_qnum++; 550 que->msqid_ds.msg_lspid = p->p_p->ps_pid; 551 que->msqid_ds.msg_stime = time_second; 552 553 TAILQ_INSERT_TAIL(&que->que_msgs, msg, msg_next); 554 } 555 556 void 557 msg_dequeue(struct que *que, struct msg *msg, struct proc *p) 558 { 559 que->msqid_ds.msg_cbytes -= msg->msg_len; 560 que->msqid_ds.msg_qnum--; 561 que->msqid_ds.msg_lrpid = p->p_p->ps_pid; 562 que->msqid_ds.msg_rtime = time_second; 563 564 TAILQ_REMOVE(&que->que_msgs, msg, msg_next); 565 } 566 567 /* 568 * The actual I/O routines. A note concerning the layout of SysV msg buffers: 569 * 570 * The data to be copied is laid out as a single userspace buffer, with a 571 * long preceding an opaque buffer of len bytes. The long value ends 572 * up being the message type, which needs to be copied separately from 573 * the buffer data, which is stored in in mbufs. 574 */ 575 576 int 577 msg_copyin(struct msg *msg, const char *ubuf, size_t len, struct proc *p) 578 { 579 struct mbuf **mm, *m; 580 size_t xfer; 581 int error; 582 583 if (msg == NULL) 584 panic ("msg NULL"); 585 586 if ((error = copyin(ubuf, &msg->msg_type, sizeof(long)))) { 587 msg_free(msg); 588 return (error); 589 } 590 591 if (msg->msg_type < 0) { 592 msg_free(msg); 593 return (EINVAL); 594 } 595 596 ubuf += sizeof(long); 597 598 msg->msg_len = 0; 599 mm = &msg->msg_data; 600 601 while (msg->msg_len < len) { 602 m = m_get(M_WAIT, MT_DATA); 603 if (len >= MINCLSIZE) { 604 MCLGET(m, M_WAIT); 605 xfer = min(len, MCLBYTES); 606 } else { 607 xfer = min(len, MLEN); 608 } 609 m->m_len = xfer; 610 msg->msg_len += xfer; 611 *mm = m; 612 mm = &m->m_next; 613 } 614 615 for (m = msg->msg_data; m; m = m->m_next) { 616 if ((error = copyin(ubuf, mtod(m, void *), m->m_len))) { 617 msg_free(msg); 618 return (error); 619 } 620 ubuf += m->m_len; 621 } 622 623 return (0); 624 } 625 626 int 627 msg_copyout(struct msg *msg, char *ubuf, size_t *len, struct proc *p) 628 { 629 struct mbuf *m; 630 size_t xfer; 631 int error; 632 633 #ifdef DIAGNOSTIC 634 if (msg->msg_len > MSGMAX) 635 panic("SysV message longer than MSGMAX"); 636 #endif 637 638 /* silently truncate messages too large for user buffer */ 639 xfer = min(*len, msg->msg_len); 640 641 if ((error = copyout(&msg->msg_type, ubuf, sizeof(long)))) 642 return (error); 643 644 ubuf += sizeof(long); 645 *len = xfer; 646 647 for (m = msg->msg_data; m; m = m->m_next) { 648 if ((error = copyout(mtod(m, void *), ubuf, m->m_len))) 649 return (error); 650 ubuf += m->m_len; 651 } 652 653 return (0); 654 } 655 656 int 657 sysctl_sysvmsg(int *name, u_int namelen, void *where, size_t *sizep) 658 { 659 struct msg_sysctl_info *info; 660 struct que *que; 661 size_t infolen, infolen0; 662 int error; 663 664 switch (*name) { 665 case KERN_SYSVIPC_MSG_INFO: 666 667 if (namelen != 1) 668 return (ENOTDIR); 669 670 /* 671 * The userland ipcs(1) utility expects to be able 672 * to iterate over at least msginfo.msgmni queues, 673 * even if those queues don't exist. This is an 674 * artifact of the previous implementation of 675 * message queues; for now, emulate this behavior 676 * until a more thorough fix can be made. 677 */ 678 infolen0 = sizeof(msginfo) + 679 msginfo.msgmni * sizeof(struct msqid_ds); 680 if (where == NULL) { 681 *sizep = infolen0; 682 return (0); 683 } 684 685 /* 686 * More special-casing due to previous implementation: 687 * if the caller just wants the msginfo struct, then 688 * sizep will point to the value sizeof(struct msginfo). 689 * In that case, only copy out the msginfo struct to 690 * the caller. 691 */ 692 if (*sizep == sizeof(struct msginfo)) 693 return (copyout(&msginfo, where, sizeof(msginfo))); 694 695 info = malloc(infolen0, M_TEMP, M_WAIT|M_ZERO); 696 697 /* if the malloc slept, this may have changed */ 698 infolen = sizeof(msginfo) + 699 msginfo.msgmni * sizeof(struct msqid_ds); 700 701 if (*sizep < infolen) { 702 free(info, M_TEMP, infolen0); 703 return (ENOMEM); 704 } 705 706 memcpy(&info->msginfo, &msginfo, sizeof(struct msginfo)); 707 708 /* 709 * Special case #3: the previous array-based implementation 710 * exported the array indices and userland has come to rely 711 * upon these indices, so keep behavior consisitent. 712 */ 713 TAILQ_FOREACH(que, &msg_queues, que_next) 714 memcpy(&info->msgids[que->que_ix], &que->msqid_ds, 715 sizeof(struct msqid_ds)); 716 717 error = copyout(info, where, infolen); 718 719 free(info, M_TEMP, infolen0); 720 721 return (error); 722 723 default: 724 return (EINVAL); 725 } 726 } 727