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