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