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