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