]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sysv_msg.c
xnu-2782.20.48.tar.gz
[apple/xnu.git] / bsd / kern / sysv_msg.c
1 /*
2 * Copyright (c) 2000-2007 Apple 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 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
48 * support for mandatory and extensible security protections. This notice
49 * is included in support of clause 2.2 (b) of the Apple Public License,
50 * Version 2.0.
51 */
52
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/kernel.h>
56 #include <sys/proc_internal.h>
57 #include <sys/kauth.h>
58 #include <sys/msg.h>
59 #include <sys/malloc.h>
60 #include <mach/mach_types.h>
61
62 #include <security/audit/audit.h>
63
64 #include <sys/filedesc.h>
65 #include <sys/file_internal.h>
66 #include <sys/sysctl.h>
67 #include <sys/sysproto.h>
68 #include <sys/ipcs.h>
69
70 #if SYSV_MSG
71
72 static int msginit(void *);
73
74 #define MSG_DEBUG
75 #undef MSG_DEBUG_OK
76
77 /* Uncomment this line to see MAC debugging output. */
78 /* #define MAC_DEBUG */
79 #if CONFIG_MACF_DEBUG
80 #define MPRINTF(a) printf(a)
81 #else
82 #define MPRINTF(a)
83 #endif
84 static void msg_freehdr(struct msg *msghdr);
85
86 typedef int sy_call_t(struct proc *, void *, int *);
87
88 /* XXX casting to (sy_call_t *) is bogus, as usual. */
89 static sy_call_t *msgcalls[] = {
90 (sy_call_t *)msgctl, (sy_call_t *)msgget,
91 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv
92 };
93
94 static int nfree_msgmaps; /* # of free map entries */
95 static short free_msgmaps; /* free map entries list head */
96 static struct msg *free_msghdrs; /* list of free msg headers */
97 char *msgpool; /* MSGMAX byte long msg buffer pool */
98 struct msgmap *msgmaps; /* MSGSEG msgmap structures */
99 struct msg *msghdrs; /* MSGTQL msg headers */
100 struct msqid_kernel *msqids; /* MSGMNI msqid_kernel structs (wrapping user_msqid_ds structs) */
101
102 static lck_grp_t *sysv_msg_subsys_lck_grp;
103 static lck_grp_attr_t *sysv_msg_subsys_lck_grp_attr;
104 static lck_attr_t *sysv_msg_subsys_lck_attr;
105 static lck_mtx_t sysv_msg_subsys_mutex;
106
107 #define SYSV_MSG_SUBSYS_LOCK() lck_mtx_lock(&sysv_msg_subsys_mutex)
108 #define SYSV_MSG_SUBSYS_UNLOCK() lck_mtx_unlock(&sysv_msg_subsys_mutex)
109
110 void sysv_msg_lock_init(void);
111
112
113 #ifdef __APPLE_API_PRIVATE
114 int msgmax, /* max chars in a message */
115 msgmni, /* max message queue identifiers */
116 msgmnb, /* max chars in a queue */
117 msgtql, /* max messages in system */
118 msgssz, /* size of a message segment (see notes above) */
119 msgseg; /* number of message segments */
120 struct msginfo msginfo = {
121 MSGMAX, /* = (MSGSSZ*MSGSEG) : max chars in a message */
122 MSGMNI, /* = 40 : max message queue identifiers */
123 MSGMNB, /* = 2048 : max chars in a queue */
124 MSGTQL, /* = 40 : max messages in system */
125 MSGSSZ, /* = 8 : size of a message segment (2^N long) */
126 MSGSEG /* = 2048 : number of message segments */
127 };
128 #endif /* __APPLE_API_PRIVATE */
129
130 /* Initialize the mutex governing access to the SysV msg subsystem */
131 __private_extern__ void
132 sysv_msg_lock_init( void )
133 {
134 sysv_msg_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
135
136 sysv_msg_subsys_lck_grp = lck_grp_alloc_init("sysv_msg_subsys_lock", sysv_msg_subsys_lck_grp_attr);
137
138 sysv_msg_subsys_lck_attr = lck_attr_alloc_init();
139 lck_mtx_init(&sysv_msg_subsys_mutex, sysv_msg_subsys_lck_grp, sysv_msg_subsys_lck_attr);
140 }
141
142 static __inline__ user_time_t
143 sysv_msgtime(void)
144 {
145 struct timeval tv;
146 microtime(&tv);
147 return (tv.tv_sec);
148 }
149
150 /*
151 * NOTE: Source and target may *NOT* overlap! (target is smaller)
152 */
153 static void
154 msqid_ds_kerneltouser32(struct user_msqid_ds *in, struct user32_msqid_ds *out)
155 {
156 out->msg_perm = in->msg_perm;
157 out->msg_qnum = in->msg_qnum;
158 out->msg_cbytes = in->msg_cbytes; /* for ipcs */
159 out->msg_qbytes = in->msg_qbytes;
160 out->msg_lspid = in->msg_lspid;
161 out->msg_lrpid = in->msg_lrpid;
162 out->msg_stime = in->msg_stime; /* XXX loss of range */
163 out->msg_rtime = in->msg_rtime; /* XXX loss of range */
164 out->msg_ctime = in->msg_ctime; /* XXX loss of range */
165 }
166
167 static void
168 msqid_ds_kerneltouser64(struct user_msqid_ds *in, struct user64_msqid_ds *out)
169 {
170 out->msg_perm = in->msg_perm;
171 out->msg_qnum = in->msg_qnum;
172 out->msg_cbytes = in->msg_cbytes; /* for ipcs */
173 out->msg_qbytes = in->msg_qbytes;
174 out->msg_lspid = in->msg_lspid;
175 out->msg_lrpid = in->msg_lrpid;
176 out->msg_stime = in->msg_stime; /* XXX loss of range */
177 out->msg_rtime = in->msg_rtime; /* XXX loss of range */
178 out->msg_ctime = in->msg_ctime; /* XXX loss of range */
179 }
180
181 /*
182 * NOTE: Source and target may are permitted to overlap! (source is smaller);
183 * this works because we copy fields in order from the end of the struct to
184 * the beginning.
185 */
186 static void
187 msqid_ds_user32tokernel(struct user32_msqid_ds *in, struct user_msqid_ds *out)
188 {
189 out->msg_ctime = in->msg_ctime;
190 out->msg_rtime = in->msg_rtime;
191 out->msg_stime = in->msg_stime;
192 out->msg_lrpid = in->msg_lrpid;
193 out->msg_lspid = in->msg_lspid;
194 out->msg_qbytes = in->msg_qbytes;
195 out->msg_cbytes = in->msg_cbytes; /* for ipcs */
196 out->msg_qnum = in->msg_qnum;
197 out->msg_perm = in->msg_perm;
198 }
199
200 static void
201 msqid_ds_user64tokernel(struct user64_msqid_ds *in, struct user_msqid_ds *out)
202 {
203 out->msg_ctime = in->msg_ctime;
204 out->msg_rtime = in->msg_rtime;
205 out->msg_stime = in->msg_stime;
206 out->msg_lrpid = in->msg_lrpid;
207 out->msg_lspid = in->msg_lspid;
208 out->msg_qbytes = in->msg_qbytes;
209 out->msg_cbytes = in->msg_cbytes; /* for ipcs */
210 out->msg_qnum = in->msg_qnum;
211 out->msg_perm = in->msg_perm;
212 }
213
214 /* This routine assumes the system is locked prior to calling this routine */
215 static int
216 msginit(__unused void *dummy)
217 {
218 static int initted = 0;
219 register int i;
220
221 /* Lazy initialization on first system call; we don't have SYSINIT(). */
222 if (initted)
223 return (initted);
224
225 /*
226 * msginfo.msgssz should be a power of two for efficiency reasons.
227 * It is also pretty silly if msginfo.msgssz is less than 8
228 * or greater than about 256 so ...
229 */
230 i = 8;
231 while (i < 1024 && i != msginfo.msgssz)
232 i <<= 1;
233 if (i != msginfo.msgssz) {
234 printf("msginfo.msgssz=%d (0x%x) not a small power of 2; resetting to %d\n", msginfo.msgssz, msginfo.msgssz, MSGSSZ);
235 msginfo.msgssz = MSGSSZ;
236 }
237
238 if (msginfo.msgseg > 32767) {
239 printf("msginfo.msgseg=%d (> 32767); resetting to %d\n", msginfo.msgseg, MSGSEG);
240 msginfo.msgseg = MSGSEG;
241 }
242
243
244 /*
245 * Allocate memory for message pool, maps, headers, and queue IDs;
246 * if this fails, fail safely and leave it uninitialized (related
247 * system calls will fail).
248 */
249 msgpool = (char *)_MALLOC(msginfo.msgmax, M_SHM, M_WAITOK);
250 if (msgpool == NULL) {
251 printf("msginit: can't allocate msgpool");
252 goto bad;
253 }
254 MALLOC(msgmaps, struct msgmap *,
255 sizeof(struct msgmap) * msginfo.msgseg,
256 M_SHM, M_WAITOK);
257 if (msgmaps == NULL) {
258 printf("msginit: can't allocate msgmaps");
259 goto bad;
260 }
261
262 MALLOC(msghdrs, struct msg *,
263 sizeof(struct msg) * msginfo.msgtql,
264 M_SHM, M_WAITOK);
265 if (msghdrs == NULL) {
266 printf("msginit: can't allocate msghdrs");
267 goto bad;
268 }
269
270 MALLOC(msqids, struct msqid_kernel *,
271 sizeof(struct user_msqid_ds) * msginfo.msgmni,
272 M_SHM, M_WAITOK);
273 if (msqids == NULL) {
274 printf("msginit: can't allocate msqids");
275 goto bad;
276 }
277
278
279 /* init msgmaps */
280 for (i = 0; i < msginfo.msgseg; i++) {
281 if (i > 0)
282 msgmaps[i-1].next = i;
283 msgmaps[i].next = -1; /* implies entry is available */
284 }
285 free_msgmaps = 0;
286 nfree_msgmaps = msginfo.msgseg;
287
288
289 /* init msghdrs */
290 for (i = 0; i < msginfo.msgtql; i++) {
291 msghdrs[i].msg_type = 0;
292 if (i > 0)
293 msghdrs[i-1].msg_next = &msghdrs[i];
294 msghdrs[i].msg_next = NULL;
295 #if CONFIG_MACF
296 mac_sysvmsg_label_init(&msghdrs[i]);
297 #endif
298 }
299 free_msghdrs = &msghdrs[0];
300
301 /* init msqids */
302 for (i = 0; i < msginfo.msgmni; i++) {
303 msqids[i].u.msg_qbytes = 0; /* implies entry is available */
304 msqids[i].u.msg_perm._seq = 0; /* reset to a known value */
305 msqids[i].u.msg_perm.mode = 0;
306 #if CONFIG_MACF
307 mac_sysvmsq_label_init(&msqids[i]);
308 #endif
309 }
310
311 initted = 1;
312 bad:
313 if (!initted) {
314 if (msgpool != NULL)
315 _FREE(msgpool, M_SHM);
316 if (msgmaps != NULL)
317 FREE(msgmaps, M_SHM);
318 if (msghdrs != NULL)
319 FREE(msghdrs, M_SHM);
320 if (msqids != NULL)
321 FREE(msqids, M_SHM);
322 }
323 return (initted);
324 }
325
326 /*
327 * msgsys
328 *
329 * Entry point for all MSG calls: msgctl, msgget, msgsnd, msgrcv
330 *
331 * Parameters: p Process requesting the call
332 * uap User argument descriptor (see below)
333 * retval Return value of the selected msg call
334 *
335 * Indirect parameters: uap->which msg call to invoke (index in array of msg calls)
336 * uap->a2 User argument descriptor
337 *
338 * Returns: 0 Success
339 * !0 Not success
340 *
341 * Implicit returns: retval Return value of the selected msg call
342 *
343 * DEPRECATED: This interface should not be used to call the other MSG
344 * functions (msgctl, msgget, msgsnd, msgrcv). The correct
345 * usage is to call the other MSG functions directly.
346 *
347 */
348 int
349 msgsys(struct proc *p, struct msgsys_args *uap, int32_t *retval)
350 {
351 if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
352 return (EINVAL);
353 return ((*msgcalls[uap->which])(p, &uap->a2, retval));
354 }
355
356 static void
357 msg_freehdr(struct msg *msghdr)
358 {
359 while (msghdr->msg_ts > 0) {
360 short next;
361 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
362 panic("msghdr->msg_spot out of range");
363 next = msgmaps[msghdr->msg_spot].next;
364 msgmaps[msghdr->msg_spot].next = free_msgmaps;
365 free_msgmaps = msghdr->msg_spot;
366 nfree_msgmaps++;
367 msghdr->msg_spot = next;
368 if (msghdr->msg_ts >= msginfo.msgssz)
369 msghdr->msg_ts -= msginfo.msgssz;
370 else
371 msghdr->msg_ts = 0;
372 }
373 if (msghdr->msg_spot != -1)
374 panic("msghdr->msg_spot != -1");
375 msghdr->msg_next = free_msghdrs;
376 free_msghdrs = msghdr;
377 #if CONFIG_MACF
378 mac_sysvmsg_label_recycle(msghdr);
379 #endif
380 /*
381 * Notify waiters that there are free message headers and segments
382 * now available.
383 */
384 wakeup((caddr_t)&free_msghdrs);
385 }
386
387 int
388 msgctl(struct proc *p, struct msgctl_args *uap, int32_t *retval)
389 {
390 int msqid = uap->msqid;
391 int cmd = uap->cmd;
392 kauth_cred_t cred = kauth_cred_get();
393 int rval, eval;
394 struct user_msqid_ds msqbuf;
395 struct msqid_kernel *msqptr;
396
397 SYSV_MSG_SUBSYS_LOCK();
398
399 if (!msginit(0)) {
400 eval = ENOMEM;
401 goto msgctlout;
402 }
403
404 #ifdef MSG_DEBUG_OK
405 printf("call to msgctl(%d, %d, 0x%qx)\n", msqid, cmd, uap->buf);
406 #endif
407
408 AUDIT_ARG(svipc_cmd, cmd);
409 AUDIT_ARG(svipc_id, msqid);
410 msqid = IPCID_TO_IX(msqid);
411
412 if (msqid < 0 || msqid >= msginfo.msgmni) {
413 #ifdef MSG_DEBUG_OK
414 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
415 msginfo.msgmni);
416 #endif
417 eval = EINVAL;
418 goto msgctlout;
419 }
420
421 msqptr = &msqids[msqid];
422
423 if (msqptr->u.msg_qbytes == 0) {
424 #ifdef MSG_DEBUG_OK
425 printf("no such msqid\n");
426 #endif
427 eval = EINVAL;
428 goto msgctlout;
429 }
430 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
431 #ifdef MSG_DEBUG_OK
432 printf("wrong sequence number\n");
433 #endif
434 eval = EINVAL;
435 goto msgctlout;
436 }
437 #if CONFIG_MACF
438 eval = mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr, cmd);
439 if (eval)
440 goto msgctlout;
441 #endif
442
443 eval = 0;
444 rval = 0;
445
446 switch (cmd) {
447
448 case IPC_RMID:
449 {
450 struct msg *msghdr;
451 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M)))
452 goto msgctlout;
453 #if CONFIG_MACF
454 /*
455 * Check that the thread has MAC access permissions to
456 * individual msghdrs. Note: We need to do this in a
457 * separate loop because the actual loop alters the
458 * msq/msghdr info as it progresses, and there is no going
459 * back if half the way through we discover that the
460 * thread cannot free a certain msghdr. The msq will get
461 * into an inconsistent state.
462 */
463 for (msghdr = msqptr->u.msg_first; msghdr != NULL;
464 msghdr = msghdr->msg_next) {
465 eval = mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr);
466 if (eval)
467 goto msgctlout;
468 }
469 #endif
470 /* Free the message headers */
471 msghdr = msqptr->u.msg_first;
472 while (msghdr != NULL) {
473 struct msg *msghdr_tmp;
474
475 /* Free the segments of each message */
476 msqptr->u.msg_cbytes -= msghdr->msg_ts;
477 msqptr->u.msg_qnum--;
478 msghdr_tmp = msghdr;
479 msghdr = msghdr->msg_next;
480 msg_freehdr(msghdr_tmp);
481 }
482
483 if (msqptr->u.msg_cbytes != 0)
484 panic("msg_cbytes is messed up");
485 if (msqptr->u.msg_qnum != 0)
486 panic("msg_qnum is messed up");
487
488 msqptr->u.msg_qbytes = 0; /* Mark it as free */
489 #if CONFIG_MACF
490 mac_sysvmsq_label_recycle(msqptr);
491 #endif
492
493 wakeup((caddr_t)msqptr);
494 }
495
496 break;
497
498 case IPC_SET:
499 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M)))
500 goto msgctlout;
501
502 SYSV_MSG_SUBSYS_UNLOCK();
503
504 if (IS_64BIT_PROCESS(p)) {
505 struct user64_msqid_ds tmpds;
506 eval = copyin(uap->buf, &tmpds, sizeof(tmpds));
507
508 msqid_ds_user64tokernel(&tmpds, &msqbuf);
509 } else {
510 struct user32_msqid_ds tmpds;
511
512 eval = copyin(uap->buf, &tmpds, sizeof(tmpds));
513
514 msqid_ds_user32tokernel(&tmpds, &msqbuf);
515 }
516 if (eval)
517 return(eval);
518
519 SYSV_MSG_SUBSYS_LOCK();
520
521 if (msqbuf.msg_qbytes > msqptr->u.msg_qbytes) {
522 eval = suser(cred, &p->p_acflag);
523 if (eval)
524 goto msgctlout;
525 }
526
527
528 /* compare (msglen_t) value against restrict (int) value */
529 if (msqbuf.msg_qbytes > (user_msglen_t)msginfo.msgmnb) {
530 #ifdef MSG_DEBUG_OK
531 printf("can't increase msg_qbytes beyond %d (truncating)\n",
532 msginfo.msgmnb);
533 #endif
534 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */
535 }
536 if (msqbuf.msg_qbytes == 0) {
537 #ifdef MSG_DEBUG_OK
538 printf("can't reduce msg_qbytes to 0\n");
539 #endif
540 eval = EINVAL;
541 goto msgctlout;
542 }
543 msqptr->u.msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */
544 msqptr->u.msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */
545 msqptr->u.msg_perm.mode = (msqptr->u.msg_perm.mode & ~0777) |
546 (msqbuf.msg_perm.mode & 0777);
547 msqptr->u.msg_qbytes = msqbuf.msg_qbytes;
548 msqptr->u.msg_ctime = sysv_msgtime();
549 break;
550
551 case IPC_STAT:
552 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_R))) {
553 #ifdef MSG_DEBUG_OK
554 printf("requester doesn't have read access\n");
555 #endif
556 goto msgctlout;
557 }
558
559 SYSV_MSG_SUBSYS_UNLOCK();
560 if (IS_64BIT_PROCESS(p)) {
561 struct user64_msqid_ds msqid_ds64;
562 msqid_ds_kerneltouser64(&msqptr->u, &msqid_ds64);
563 eval = copyout(&msqid_ds64, uap->buf, sizeof(msqid_ds64));
564 } else {
565 struct user32_msqid_ds msqid_ds32;
566 msqid_ds_kerneltouser32(&msqptr->u, &msqid_ds32);
567 eval = copyout(&msqid_ds32, uap->buf, sizeof(msqid_ds32));
568 }
569 SYSV_MSG_SUBSYS_LOCK();
570 break;
571
572 default:
573 #ifdef MSG_DEBUG_OK
574 printf("invalid command %d\n", cmd);
575 #endif
576 eval = EINVAL;
577 goto msgctlout;
578 }
579
580 if (eval == 0)
581 *retval = rval;
582 msgctlout:
583 SYSV_MSG_SUBSYS_UNLOCK();
584 return(eval);
585 }
586
587 int
588 msgget(__unused struct proc *p, struct msgget_args *uap, int32_t *retval)
589 {
590 int msqid, eval;
591 int key = uap->key;
592 int msgflg = uap->msgflg;
593 kauth_cred_t cred = kauth_cred_get();
594 struct msqid_kernel *msqptr = NULL;
595
596 SYSV_MSG_SUBSYS_LOCK();
597
598 if (!msginit(0)) {
599 eval = ENOMEM;
600 goto msggetout;
601 }
602
603 #ifdef MSG_DEBUG_OK
604 printf("msgget(0x%x, 0%o)\n", key, msgflg);
605 #endif
606
607 if (key != IPC_PRIVATE) {
608 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
609 msqptr = &msqids[msqid];
610 if (msqptr->u.msg_qbytes != 0 &&
611 msqptr->u.msg_perm._key == key)
612 break;
613 }
614 if (msqid < msginfo.msgmni) {
615 #ifdef MSG_DEBUG_OK
616 printf("found public key\n");
617 #endif
618 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
619 #ifdef MSG_DEBUG_OK
620 printf("not exclusive\n");
621 #endif
622 eval = EEXIST;
623 goto msggetout;
624 }
625 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, msgflg & 0700 ))) {
626 #ifdef MSG_DEBUG_OK
627 printf("requester doesn't have 0%o access\n",
628 msgflg & 0700);
629 #endif
630 goto msggetout;
631 }
632 #if CONFIG_MACF
633 eval = mac_sysvmsq_check_msqget(cred, msqptr);
634 if (eval)
635 goto msggetout;
636 #endif
637 goto found;
638 }
639 }
640
641 #ifdef MSG_DEBUG_OK
642 printf("need to allocate the user_msqid_ds\n");
643 #endif
644 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
645 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
646 /*
647 * Look for an unallocated and unlocked user_msqid_ds.
648 * user_msqid_ds's can be locked by msgsnd or msgrcv
649 * while they are copying the message in/out. We
650 * can't re-use the entry until they release it.
651 */
652 msqptr = &msqids[msqid];
653 if (msqptr->u.msg_qbytes == 0 &&
654 (msqptr->u.msg_perm.mode & MSG_LOCKED) == 0)
655 break;
656 }
657 if (msqid == msginfo.msgmni) {
658 #ifdef MSG_DEBUG_OK
659 printf("no more user_msqid_ds's available\n");
660 #endif
661 eval = ENOSPC;
662 goto msggetout;
663 }
664 #ifdef MSG_DEBUG_OK
665 printf("msqid %d is available\n", msqid);
666 #endif
667 msqptr->u.msg_perm._key = key;
668 msqptr->u.msg_perm.cuid = kauth_cred_getuid(cred);
669 msqptr->u.msg_perm.uid = kauth_cred_getuid(cred);
670 msqptr->u.msg_perm.cgid = kauth_cred_getgid(cred);
671 msqptr->u.msg_perm.gid = kauth_cred_getgid(cred);
672 msqptr->u.msg_perm.mode = (msgflg & 0777);
673 /* Make sure that the returned msqid is unique */
674 msqptr->u.msg_perm._seq++;
675 msqptr->u.msg_first = NULL;
676 msqptr->u.msg_last = NULL;
677 msqptr->u.msg_cbytes = 0;
678 msqptr->u.msg_qnum = 0;
679 msqptr->u.msg_qbytes = msginfo.msgmnb;
680 msqptr->u.msg_lspid = 0;
681 msqptr->u.msg_lrpid = 0;
682 msqptr->u.msg_stime = 0;
683 msqptr->u.msg_rtime = 0;
684 msqptr->u.msg_ctime = sysv_msgtime();
685 #if CONFIG_MACF
686 mac_sysvmsq_label_associate(cred, msqptr);
687 #endif
688 } else {
689 #ifdef MSG_DEBUG_OK
690 printf("didn't find it and wasn't asked to create it\n");
691 #endif
692 eval = ENOENT;
693 goto msggetout;
694 }
695
696 found:
697 /* Construct the unique msqid */
698 *retval = IXSEQ_TO_IPCID(msqid, msqptr->u.msg_perm);
699 AUDIT_ARG(svipc_id, *retval);
700 eval = 0;
701 msggetout:
702 SYSV_MSG_SUBSYS_UNLOCK();
703 return(eval);
704 }
705
706
707 int
708 msgsnd(struct proc *p, struct msgsnd_args *uap, int32_t *retval)
709 {
710 __pthread_testcancel(1);
711 return(msgsnd_nocancel(p, (struct msgsnd_nocancel_args *)uap, retval));
712 }
713
714 int
715 msgsnd_nocancel(struct proc *p, struct msgsnd_nocancel_args *uap, int32_t *retval)
716 {
717 int msqid = uap->msqid;
718 user_addr_t user_msgp = uap->msgp;
719 size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */
720 int msgflg = uap->msgflg;
721 int segs_needed, eval;
722 struct msqid_kernel *msqptr;
723 struct msg *msghdr;
724 short next;
725 user_long_t msgtype;
726
727
728 SYSV_MSG_SUBSYS_LOCK();
729
730 if (!msginit(0)) {
731 eval = ENOMEM;
732 goto msgsndout;
733 }
734
735 #ifdef MSG_DEBUG_OK
736 printf("call to msgsnd(%d, 0x%qx, %ld, %d)\n", msqid, user_msgp, msgsz,
737 msgflg);
738 #endif
739
740 AUDIT_ARG(svipc_id, msqid);
741 msqid = IPCID_TO_IX(msqid);
742
743 if (msqid < 0 || msqid >= msginfo.msgmni) {
744 #ifdef MSG_DEBUG_OK
745 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
746 msginfo.msgmni);
747 #endif
748 eval = EINVAL;
749 goto msgsndout;
750 }
751
752 msqptr = &msqids[msqid];
753 if (msqptr->u.msg_qbytes == 0) {
754 #ifdef MSG_DEBUG_OK
755 printf("no such message queue id\n");
756 #endif
757 eval = EINVAL;
758 goto msgsndout;
759 }
760 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
761 #ifdef MSG_DEBUG_OK
762 printf("wrong sequence number\n");
763 #endif
764 eval = EINVAL;
765 goto msgsndout;
766 }
767
768 if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_W))) {
769 #ifdef MSG_DEBUG_OK
770 printf("requester doesn't have write access\n");
771 #endif
772 goto msgsndout;
773 }
774
775 #if CONFIG_MACF
776 eval = mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr);
777 if (eval)
778 goto msgsndout;
779 #endif
780 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
781 #ifdef MSG_DEBUG_OK
782 printf("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
783 segs_needed);
784 #endif
785
786 /*
787 * If we suffer resource starvation, we will sleep in this loop and
788 * wait for more resources to become available. This is a loop to
789 * ensure reacquisition of the mutex following any sleep, since there
790 * are multiple resources under contention.
791 */
792 for (;;) {
793 void *blocking_resource = NULL;
794
795 /*
796 * Check that we have not had the maximum message size change
797 * out from under us and render our message invalid while we
798 * slept waiting for some resource.
799 */
800 if (msgsz > msqptr->u.msg_qbytes) {
801 #ifdef MSG_DEBUG_OK
802 printf("msgsz > msqptr->msg_qbytes\n");
803 #endif
804 eval = EINVAL;
805 goto msgsndout;
806 }
807
808 /*
809 * If the user_msqid_ds is already locked, we need to sleep on
810 * the queue until it's unlocked.
811 */
812 if (msqptr->u.msg_perm.mode & MSG_LOCKED) {
813 #ifdef MSG_DEBUG_OK
814 printf("msqid is locked\n");
815 #endif
816 blocking_resource = msqptr;
817 }
818
819 /*
820 * If our message plus the messages already in the queue would
821 * cause us to exceed the maximum number of bytes wer are
822 * permitted to queue, then block on the queue until it drains.
823 */
824 if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) {
825 #ifdef MSG_DEBUG_OK
826 printf("msgsz + msg_cbytes > msg_qbytes\n");
827 #endif
828 blocking_resource = msqptr;
829 }
830
831 /*
832 * Both message maps and message headers are protected by
833 * sleeping on the address of the pointer to the list of free
834 * message headers, since they are allocated and freed in
835 * tandem.
836 */
837 if (segs_needed > nfree_msgmaps) {
838 #ifdef MSG_DEBUG_OK
839 printf("segs_needed > nfree_msgmaps\n");
840 #endif
841 blocking_resource = &free_msghdrs;
842 }
843 if (free_msghdrs == NULL) {
844 #ifdef MSG_DEBUG_OK
845 printf("no more msghdrs\n");
846 #endif
847 blocking_resource = &free_msghdrs;
848 }
849
850 if (blocking_resource != NULL) {
851 int we_own_it;
852
853 if ((msgflg & IPC_NOWAIT) != 0) {
854 #ifdef MSG_DEBUG_OK
855 printf("need more resources but caller doesn't want to wait\n");
856 #endif
857 eval = EAGAIN;
858 goto msgsndout;
859 }
860
861 if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
862 #ifdef MSG_DEBUG_OK
863 printf("we don't own the user_msqid_ds\n");
864 #endif
865 we_own_it = 0;
866 } else {
867 /* Force later arrivals to wait for our
868 request */
869 #ifdef MSG_DEBUG_OK
870 printf("we own the user_msqid_ds\n");
871 #endif
872 msqptr->u.msg_perm.mode |= MSG_LOCKED;
873 we_own_it = 1;
874 }
875 #ifdef MSG_DEBUG_OK
876 printf("goodnight\n");
877 #endif
878 eval = msleep(blocking_resource, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH,
879 "msgwait", 0);
880 #ifdef MSG_DEBUG_OK
881 printf("good morning, eval=%d\n", eval);
882 #endif
883 if (we_own_it)
884 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
885 if (eval != 0) {
886 #ifdef MSG_DEBUG_OK
887 printf("msgsnd: interrupted system call\n");
888 #endif
889 eval = EINTR;
890 goto msgsndout;
891 }
892
893 /*
894 * Make sure that the msq queue still exists
895 */
896
897 if (msqptr->u.msg_qbytes == 0) {
898 #ifdef MSG_DEBUG_OK
899 printf("msqid deleted\n");
900 #endif
901 eval = EIDRM;
902 goto msgsndout;
903
904 }
905
906 } else {
907 #ifdef MSG_DEBUG_OK
908 printf("got all the resources that we need\n");
909 #endif
910 break;
911 }
912 }
913
914 /*
915 * We have the resources that we need.
916 * Make sure!
917 */
918
919 if (msqptr->u.msg_perm.mode & MSG_LOCKED)
920 panic("msg_perm.mode & MSG_LOCKED");
921 if (segs_needed > nfree_msgmaps)
922 panic("segs_needed > nfree_msgmaps");
923 if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes)
924 panic("msgsz + msg_cbytes > msg_qbytes");
925 if (free_msghdrs == NULL)
926 panic("no more msghdrs");
927
928 /*
929 * Re-lock the user_msqid_ds in case we page-fault when copying in
930 * the message
931 */
932 if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0)
933 panic("user_msqid_ds is already locked");
934 msqptr->u.msg_perm.mode |= MSG_LOCKED;
935
936 /*
937 * Allocate a message header
938 */
939 msghdr = free_msghdrs;
940 free_msghdrs = msghdr->msg_next;
941 msghdr->msg_spot = -1;
942 msghdr->msg_ts = msgsz;
943
944 #if CONFIG_MACF
945 mac_sysvmsg_label_associate(kauth_cred_get(), msqptr, msghdr);
946 #endif
947 /*
948 * Allocate space for the message
949 */
950
951 while (segs_needed > 0) {
952 if (nfree_msgmaps <= 0)
953 panic("not enough msgmaps");
954 if (free_msgmaps == -1)
955 panic("nil free_msgmaps");
956 next = free_msgmaps;
957 if (next <= -1)
958 panic("next too low #1");
959 if (next >= msginfo.msgseg)
960 panic("next out of range #1");
961 #ifdef MSG_DEBUG_OK
962 printf("allocating segment %d to message\n", next);
963 #endif
964 free_msgmaps = msgmaps[next].next;
965 nfree_msgmaps--;
966 msgmaps[next].next = msghdr->msg_spot;
967 msghdr->msg_spot = next;
968 segs_needed--;
969 }
970
971 /*
972 * Copy in the message type. For a 64 bit process, this is 64 bits,
973 * but we only ever use the low 32 bits, so the cast is OK.
974 */
975 if (IS_64BIT_PROCESS(p)) {
976 SYSV_MSG_SUBSYS_UNLOCK();
977 eval = copyin(user_msgp, &msgtype, sizeof(msgtype));
978 SYSV_MSG_SUBSYS_LOCK();
979 msghdr->msg_type = CAST_DOWN(long,msgtype);
980 user_msgp = user_msgp + sizeof(msgtype); /* ptr math */
981 } else {
982 SYSV_MSG_SUBSYS_UNLOCK();
983 int32_t msg_type32;
984 eval = copyin(user_msgp, &msg_type32, sizeof(msg_type32));
985 msghdr->msg_type = msg_type32;
986 SYSV_MSG_SUBSYS_LOCK();
987 user_msgp = user_msgp + sizeof(msg_type32); /* ptr math */
988 }
989
990 if (eval != 0) {
991 #ifdef MSG_DEBUG_OK
992 printf("error %d copying the message type\n", eval);
993 #endif
994 msg_freehdr(msghdr);
995 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
996 wakeup((caddr_t)msqptr);
997 goto msgsndout;
998 }
999
1000
1001 /*
1002 * Validate the message type
1003 */
1004 if (msghdr->msg_type < 1) {
1005 msg_freehdr(msghdr);
1006 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1007 wakeup((caddr_t)msqptr);
1008 #ifdef MSG_DEBUG_OK
1009 printf("mtype (%ld) < 1\n", msghdr->msg_type);
1010 #endif
1011 eval = EINVAL;
1012 goto msgsndout;
1013 }
1014
1015 /*
1016 * Copy in the message body
1017 */
1018 next = msghdr->msg_spot;
1019 while (msgsz > 0) {
1020 size_t tlen;
1021 /* compare input (size_t) value against restrict (int) value */
1022 if (msgsz > (size_t)msginfo.msgssz)
1023 tlen = msginfo.msgssz;
1024 else
1025 tlen = msgsz;
1026 if (next <= -1)
1027 panic("next too low #2");
1028 if (next >= msginfo.msgseg)
1029 panic("next out of range #2");
1030
1031 SYSV_MSG_SUBSYS_UNLOCK();
1032 eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen);
1033 SYSV_MSG_SUBSYS_LOCK();
1034
1035 if (eval != 0) {
1036 #ifdef MSG_DEBUG_OK
1037 printf("error %d copying in message segment\n", eval);
1038 #endif
1039 msg_freehdr(msghdr);
1040 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1041 wakeup((caddr_t)msqptr);
1042
1043 goto msgsndout;
1044 }
1045 msgsz -= tlen;
1046 user_msgp = user_msgp + tlen; /* ptr math */
1047 next = msgmaps[next].next;
1048 }
1049 if (next != -1)
1050 panic("didn't use all the msg segments");
1051
1052 /*
1053 * We've got the message. Unlock the user_msqid_ds.
1054 */
1055
1056 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1057
1058 /*
1059 * Make sure that the user_msqid_ds is still allocated.
1060 */
1061
1062 if (msqptr->u.msg_qbytes == 0) {
1063 msg_freehdr(msghdr);
1064 wakeup((caddr_t)msqptr);
1065 /* The SVID says to return EIDRM. */
1066 #ifdef EIDRM
1067 eval = EIDRM;
1068 #else
1069 /* Unfortunately, BSD doesn't define that code yet! */
1070 eval = EINVAL;
1071 #endif
1072 goto msgsndout;
1073 }
1074
1075 #if CONFIG_MACF
1076 /*
1077 * Note: Since the task/thread allocates the msghdr and usually
1078 * primes it with its own MAC label, for a majority of policies, it
1079 * won't be necessary to check whether the msghdr has access
1080 * permissions to the msgq. The mac_sysvmsq_check_msqsnd check would
1081 * suffice in that case. However, this hook may be required where
1082 * individual policies derive a non-identical label for the msghdr
1083 * from the current thread label and may want to check the msghdr
1084 * enqueue permissions, along with read/write permissions to the
1085 * msgq.
1086 */
1087 eval = mac_sysvmsq_check_enqueue(kauth_cred_get(), msghdr, msqptr);
1088 if (eval) {
1089 msg_freehdr(msghdr);
1090 wakeup((caddr_t) msqptr);
1091 goto msgsndout;
1092 }
1093 #endif
1094 /*
1095 * Put the message into the queue
1096 */
1097
1098 if (msqptr->u.msg_first == NULL) {
1099 msqptr->u.msg_first = msghdr;
1100 msqptr->u.msg_last = msghdr;
1101 } else {
1102 msqptr->u.msg_last->msg_next = msghdr;
1103 msqptr->u.msg_last = msghdr;
1104 }
1105 msqptr->u.msg_last->msg_next = NULL;
1106
1107 msqptr->u.msg_cbytes += msghdr->msg_ts;
1108 msqptr->u.msg_qnum++;
1109 msqptr->u.msg_lspid = p->p_pid;
1110 msqptr->u.msg_stime = sysv_msgtime();
1111
1112 wakeup((caddr_t)msqptr);
1113 *retval = 0;
1114 eval = 0;
1115
1116 msgsndout:
1117 SYSV_MSG_SUBSYS_UNLOCK();
1118 return(eval);
1119 }
1120
1121
1122 int
1123 msgrcv(struct proc *p, struct msgrcv_args *uap, user_ssize_t *retval)
1124 {
1125 __pthread_testcancel(1);
1126 return(msgrcv_nocancel(p, (struct msgrcv_nocancel_args *)uap, retval));
1127 }
1128
1129 int
1130 msgrcv_nocancel(struct proc *p, struct msgrcv_nocancel_args *uap, user_ssize_t *retval)
1131 {
1132 int msqid = uap->msqid;
1133 user_addr_t user_msgp = uap->msgp;
1134 size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */
1135 long msgtyp = (long)uap->msgtyp; /* limit to 32 bits */
1136 int msgflg = uap->msgflg;
1137 size_t len;
1138 struct msqid_kernel *msqptr;
1139 struct msg *msghdr;
1140 int eval;
1141 short next;
1142 user_long_t msgtype;
1143 int32_t msg_type32;
1144
1145 SYSV_MSG_SUBSYS_LOCK();
1146
1147 if (!msginit(0)) {
1148 eval = ENOMEM;
1149 goto msgrcvout;
1150 }
1151
1152 #ifdef MSG_DEBUG_OK
1153 printf("call to msgrcv(%d, 0x%qx, %ld, %ld, %d)\n", msqid, user_msgp,
1154 msgsz, msgtyp, msgflg);
1155 #endif
1156
1157 AUDIT_ARG(svipc_id, msqid);
1158 msqid = IPCID_TO_IX(msqid);
1159
1160 if (msqid < 0 || msqid >= msginfo.msgmni) {
1161 #ifdef MSG_DEBUG_OK
1162 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
1163 msginfo.msgmni);
1164 #endif
1165 eval = EINVAL;
1166 goto msgrcvout;
1167 }
1168
1169 msqptr = &msqids[msqid];
1170 if (msqptr->u.msg_qbytes == 0) {
1171 #ifdef MSG_DEBUG_OK
1172 printf("no such message queue id\n");
1173 #endif
1174 eval = EINVAL;
1175 goto msgrcvout;
1176 }
1177 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1178 #ifdef MSG_DEBUG_OK
1179 printf("wrong sequence number\n");
1180 #endif
1181 eval = EINVAL;
1182 goto msgrcvout;
1183 }
1184
1185 if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_R))) {
1186 #ifdef MSG_DEBUG_OK
1187 printf("requester doesn't have read access\n");
1188 #endif
1189 goto msgrcvout;
1190 }
1191
1192 #if CONFIG_MACF
1193 eval = mac_sysvmsq_check_msqrcv(kauth_cred_get(), msqptr);
1194 if (eval)
1195 goto msgrcvout;
1196 #endif
1197 msghdr = NULL;
1198 while (msghdr == NULL) {
1199 if (msgtyp == 0) {
1200 msghdr = msqptr->u.msg_first;
1201 if (msghdr != NULL) {
1202 if (msgsz < msghdr->msg_ts &&
1203 (msgflg & MSG_NOERROR) == 0) {
1204 #ifdef MSG_DEBUG_OK
1205 printf("first message on the queue is too big (want %ld, got %d)\n",
1206 msgsz, msghdr->msg_ts);
1207 #endif
1208 eval = E2BIG;
1209 goto msgrcvout;
1210 }
1211 #if CONFIG_MACF
1212 eval = mac_sysvmsq_check_msgrcv(kauth_cred_get(),
1213 msghdr);
1214 if (eval)
1215 goto msgrcvout;
1216 #endif
1217 if (msqptr->u.msg_first == msqptr->u.msg_last) {
1218 msqptr->u.msg_first = NULL;
1219 msqptr->u.msg_last = NULL;
1220 } else {
1221 msqptr->u.msg_first = msghdr->msg_next;
1222 if (msqptr->u.msg_first == NULL)
1223 panic("msg_first/last messed up #1");
1224 }
1225 }
1226 } else {
1227 struct msg *previous;
1228 struct msg **prev;
1229
1230 previous = NULL;
1231 prev = &(msqptr->u.msg_first);
1232 while ((msghdr = *prev) != NULL) {
1233 /*
1234 * Is this message's type an exact match or is
1235 * this message's type less than or equal to
1236 * the absolute value of a negative msgtyp?
1237 * Note that the second half of this test can
1238 * NEVER be true if msgtyp is positive since
1239 * msg_type is always positive!
1240 */
1241
1242 if (msgtyp == msghdr->msg_type ||
1243 msghdr->msg_type <= -msgtyp) {
1244 #ifdef MSG_DEBUG_OK
1245 printf("found message type %ld, requested %ld\n",
1246 msghdr->msg_type, msgtyp);
1247 #endif
1248 if (msgsz < msghdr->msg_ts &&
1249 (msgflg & MSG_NOERROR) == 0) {
1250 #ifdef MSG_DEBUG_OK
1251 printf("requested message on the queue is too big (want %ld, got %d)\n",
1252 msgsz, msghdr->msg_ts);
1253 #endif
1254 eval = E2BIG;
1255 goto msgrcvout;
1256 }
1257 #if CONFIG_MACF
1258 eval = mac_sysvmsq_check_msgrcv(
1259 kauth_cred_get(), msghdr);
1260 if (eval)
1261 goto msgrcvout;
1262 #endif
1263 *prev = msghdr->msg_next;
1264 if (msghdr == msqptr->u.msg_last) {
1265 if (previous == NULL) {
1266 if (prev !=
1267 &msqptr->u.msg_first)
1268 panic("msg_first/last messed up #2");
1269 msqptr->u.msg_first =
1270 NULL;
1271 msqptr->u.msg_last =
1272 NULL;
1273 } else {
1274 if (prev ==
1275 &msqptr->u.msg_first)
1276 panic("msg_first/last messed up #3");
1277 msqptr->u.msg_last =
1278 previous;
1279 }
1280 }
1281 break;
1282 }
1283 previous = msghdr;
1284 prev = &(msghdr->msg_next);
1285 }
1286 }
1287
1288 /*
1289 * We've either extracted the msghdr for the appropriate
1290 * message or there isn't one.
1291 * If there is one then bail out of this loop.
1292 */
1293
1294 if (msghdr != NULL)
1295 break;
1296
1297 /*
1298 * Hmph! No message found. Does the user want to wait?
1299 */
1300
1301 if ((msgflg & IPC_NOWAIT) != 0) {
1302 #ifdef MSG_DEBUG_OK
1303 printf("no appropriate message found (msgtyp=%ld)\n",
1304 msgtyp);
1305 #endif
1306 /* The SVID says to return ENOMSG. */
1307 #ifdef ENOMSG
1308 eval = ENOMSG;
1309 #else
1310 /* Unfortunately, BSD doesn't define that code yet! */
1311 eval = EAGAIN;
1312 #endif
1313 goto msgrcvout;
1314 }
1315
1316 /*
1317 * Wait for something to happen
1318 */
1319
1320 #ifdef MSG_DEBUG_OK
1321 printf("msgrcv: goodnight\n");
1322 #endif
1323 eval = msleep((caddr_t)msqptr, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH, "msgwait",
1324 0);
1325 #ifdef MSG_DEBUG_OK
1326 printf("msgrcv: good morning (eval=%d)\n", eval);
1327 #endif
1328
1329 if (eval != 0) {
1330 #ifdef MSG_DEBUG_OK
1331 printf("msgsnd: interrupted system call\n");
1332 #endif
1333 eval = EINTR;
1334 goto msgrcvout;
1335 }
1336
1337 /*
1338 * Make sure that the msq queue still exists
1339 */
1340
1341 if (msqptr->u.msg_qbytes == 0 ||
1342 msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1343 #ifdef MSG_DEBUG_OK
1344 printf("msqid deleted\n");
1345 #endif
1346 /* The SVID says to return EIDRM. */
1347 #ifdef EIDRM
1348 eval = EIDRM;
1349 #else
1350 /* Unfortunately, BSD doesn't define that code yet! */
1351 eval = EINVAL;
1352 #endif
1353 goto msgrcvout;
1354 }
1355 }
1356
1357 /*
1358 * Return the message to the user.
1359 *
1360 * First, do the bookkeeping (before we risk being interrupted).
1361 */
1362
1363 msqptr->u.msg_cbytes -= msghdr->msg_ts;
1364 msqptr->u.msg_qnum--;
1365 msqptr->u.msg_lrpid = p->p_pid;
1366 msqptr->u.msg_rtime = sysv_msgtime();
1367
1368 /*
1369 * Make msgsz the actual amount that we'll be returning.
1370 * Note that this effectively truncates the message if it is too long
1371 * (since msgsz is never increased).
1372 */
1373
1374 #ifdef MSG_DEBUG_OK
1375 printf("found a message, msgsz=%ld, msg_ts=%d\n", msgsz,
1376 msghdr->msg_ts);
1377 #endif
1378 if (msgsz > msghdr->msg_ts)
1379 msgsz = msghdr->msg_ts;
1380
1381 /*
1382 * Return the type to the user.
1383 */
1384
1385 /*
1386 * Copy out the message type. For a 64 bit process, this is 64 bits,
1387 * but we only ever use the low 32 bits, so the cast is OK.
1388 */
1389 if (IS_64BIT_PROCESS(p)) {
1390 msgtype = msghdr->msg_type;
1391 SYSV_MSG_SUBSYS_UNLOCK();
1392 eval = copyout(&msgtype, user_msgp, sizeof(msgtype));
1393 SYSV_MSG_SUBSYS_LOCK();
1394 user_msgp = user_msgp + sizeof(msgtype); /* ptr math */
1395 } else {
1396 msg_type32 = msghdr->msg_type;
1397 SYSV_MSG_SUBSYS_UNLOCK();
1398 eval = copyout(&msg_type32, user_msgp, sizeof(msg_type32));
1399 SYSV_MSG_SUBSYS_LOCK();
1400 user_msgp = user_msgp + sizeof(msg_type32); /* ptr math */
1401 }
1402
1403 if (eval != 0) {
1404 #ifdef MSG_DEBUG_OK
1405 printf("error (%d) copying out message type\n", eval);
1406 #endif
1407 msg_freehdr(msghdr);
1408 wakeup((caddr_t)msqptr);
1409
1410 goto msgrcvout;
1411 }
1412
1413
1414 /*
1415 * Return the segments to the user
1416 */
1417
1418 next = msghdr->msg_spot;
1419 for (len = 0; len < msgsz; len += msginfo.msgssz) {
1420 size_t tlen;
1421
1422 /* compare input (size_t) value against restrict (int) value */
1423 if (msgsz > (size_t)msginfo.msgssz)
1424 tlen = msginfo.msgssz;
1425 else
1426 tlen = msgsz;
1427 if (next <= -1)
1428 panic("next too low #3");
1429 if (next >= msginfo.msgseg)
1430 panic("next out of range #3");
1431 SYSV_MSG_SUBSYS_UNLOCK();
1432 eval = copyout(&msgpool[next * msginfo.msgssz],
1433 user_msgp, tlen);
1434 SYSV_MSG_SUBSYS_LOCK();
1435 if (eval != 0) {
1436 #ifdef MSG_DEBUG_OK
1437 printf("error (%d) copying out message segment\n",
1438 eval);
1439 #endif
1440 msg_freehdr(msghdr);
1441 wakeup((caddr_t)msqptr);
1442 goto msgrcvout;
1443 }
1444 user_msgp = user_msgp + tlen; /* ptr math */
1445 next = msgmaps[next].next;
1446 }
1447
1448 /*
1449 * Done, return the actual number of bytes copied out.
1450 */
1451
1452 msg_freehdr(msghdr);
1453 wakeup((caddr_t)msqptr);
1454 *retval = msgsz;
1455 eval = 0;
1456 msgrcvout:
1457 SYSV_MSG_SUBSYS_UNLOCK();
1458 return(eval);
1459 }
1460
1461 static int
1462 IPCS_msg_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
1463 __unused int arg2, struct sysctl_req *req)
1464 {
1465 int error;
1466 int cursor;
1467 union {
1468 struct user32_IPCS_command u32;
1469 struct user_IPCS_command u64;
1470 } ipcs;
1471 struct user32_msqid_ds msqid_ds32; /* post conversion, 32 bit version */
1472 struct user64_msqid_ds msqid_ds64; /* post conversion, 64 bit version */
1473 void *msqid_dsp;
1474 size_t ipcs_sz;
1475 size_t msqid_ds_sz;
1476 struct proc *p = current_proc();
1477
1478 if (IS_64BIT_PROCESS(p)) {
1479 ipcs_sz = sizeof(struct user_IPCS_command);
1480 msqid_ds_sz = sizeof(struct user64_msqid_ds);
1481 } else {
1482 ipcs_sz = sizeof(struct user32_IPCS_command);
1483 msqid_ds_sz = sizeof(struct user32_msqid_ds);
1484 }
1485
1486 /* Copy in the command structure */
1487 if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) {
1488 return(error);
1489 }
1490
1491 if (!IS_64BIT_PROCESS(p)) /* convert in place */
1492 ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data);
1493
1494 /* Let us version this interface... */
1495 if (ipcs.u64.ipcs_magic != IPCS_MAGIC) {
1496 return(EINVAL);
1497 }
1498
1499 SYSV_MSG_SUBSYS_LOCK();
1500
1501 switch(ipcs.u64.ipcs_op) {
1502 case IPCS_MSG_CONF: /* Obtain global configuration data */
1503 if (ipcs.u64.ipcs_datalen != sizeof(struct msginfo)) {
1504 error = ERANGE;
1505 break;
1506 }
1507 if (ipcs.u64.ipcs_cursor != 0) { /* fwd. compat. */
1508 error = EINVAL;
1509 break;
1510 }
1511 SYSV_MSG_SUBSYS_UNLOCK();
1512 error = copyout(&msginfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1513 SYSV_MSG_SUBSYS_LOCK();
1514 break;
1515
1516 case IPCS_MSG_ITER: /* Iterate over existing segments */
1517 /* Not done up top so we can set limits via sysctl (later) */
1518 if (!msginit(0)) {
1519 error = ENOMEM;
1520 break;
1521 }
1522
1523 cursor = ipcs.u64.ipcs_cursor;
1524 if (cursor < 0 || cursor >= msginfo.msgmni) {
1525 error = ERANGE;
1526 break;
1527 }
1528 if (ipcs.u64.ipcs_datalen != (int)msqid_ds_sz) {
1529 error = EINVAL;
1530 break;
1531 }
1532 for( ; cursor < msginfo.msgmni; cursor++) {
1533 if (msqids[cursor].u.msg_qbytes != 0) /* allocated */
1534 break;
1535 continue;
1536 }
1537 if (cursor == msginfo.msgmni) {
1538 error = ENOENT;
1539 break;
1540 }
1541
1542 msqid_dsp = &msqids[cursor]; /* default: 64 bit */
1543
1544 /*
1545 * If necessary, convert the 64 bit kernel segment
1546 * descriptor to a 32 bit user one.
1547 */
1548 if (IS_64BIT_PROCESS(p)) {
1549 msqid_ds_kerneltouser64(msqid_dsp, &msqid_ds64);
1550 msqid_dsp = &msqid_ds64;
1551 } else {
1552 msqid_ds_kerneltouser32(msqid_dsp, &msqid_ds32);
1553 msqid_dsp = &msqid_ds32;
1554 }
1555
1556 SYSV_MSG_SUBSYS_UNLOCK();
1557 error = copyout(msqid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1558 if (!error) {
1559 /* update cursor */
1560 ipcs.u64.ipcs_cursor = cursor + 1;
1561
1562 if (!IS_64BIT_PROCESS(p)) /* convert in place */
1563 ipcs.u32.ipcs_data = CAST_DOWN_EXPLICIT(user32_addr_t,ipcs.u64.ipcs_data);
1564 error = SYSCTL_OUT(req, &ipcs, ipcs_sz);
1565 }
1566 SYSV_MSG_SUBSYS_LOCK();
1567 break;
1568
1569 default:
1570 error = EINVAL;
1571 break;
1572 }
1573
1574 SYSV_MSG_SUBSYS_UNLOCK();
1575 return(error);
1576 }
1577
1578 SYSCTL_DECL(_kern_sysv_ipcs);
1579 SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, msg, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
1580 0, 0, IPCS_msg_sysctl,
1581 "S,IPCS_msg_command",
1582 "ipcs msg command interface");
1583
1584 #endif /* SYSV_MSG */