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