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