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