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