2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Implementation of SVID messages
31 * Author: Daniel Boulet
33 * Copyright 1993 Daniel Boulet and RTMX Inc.
35 * This system call was implemented by Daniel Boulet under contract from RTMX.
37 * Redistribution and use in source forms, with and without modification,
38 * are permitted provided that this entire comment appears intact.
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.
44 * This software is provided ``AS IS'' without any warranties of any kind.
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,
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>
59 #include <sys/malloc.h>
60 #include <mach/mach_types.h>
62 #include <bsm/audit_kernel.h>
64 #include <sys/filedesc.h>
65 #include <sys/file_internal.h>
66 #include <sys/sysctl.h>
67 #include <sys/sysproto.h>
72 static int msginit(void *);
77 /* Uncomment this line to see MAC debugging output. */
78 /* #define MAC_DEBUG */
80 #define MPRINTF(a) printf(a)
84 static void msg_freehdr(struct msg
*msghdr
);
86 typedef int sy_call_t(struct proc
*, void *, int *);
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
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) */
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
;
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)
110 void sysv_msg_lock_init(void);
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 */
128 #endif /* __APPLE_API_PRIVATE */
130 /* Initialize the mutex governing access to the SysV msg subsystem */
131 __private_extern__
void
132 sysv_msg_lock_init( void )
134 sysv_msg_subsys_lck_grp_attr
= lck_grp_attr_alloc_init();
136 sysv_msg_subsys_lck_grp
= lck_grp_alloc_init("sysv_msg_subsys_lock", sysv_msg_subsys_lck_grp_attr
);
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
);
142 static __inline__ user_time_t
151 * NOTE: Source and target may *NOT* overlap! (target is smaller)
154 msqid_ds_64to32(struct user_msqid_ds
*in
, struct msqid_ds
*out
)
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 */
168 * NOTE: Source and target may are permitted to overlap! (source is smaller);
169 * this works because we copy fields in order from the end of the struct to
173 msqid_ds_32to64(struct msqid_ds
*in
, struct user_msqid_ds
*out
)
175 out
->msg_ctime
= in
->msg_ctime
;
176 out
->msg_rtime
= in
->msg_rtime
;
177 out
->msg_stime
= in
->msg_stime
;
178 out
->msg_lrpid
= in
->msg_lrpid
;
179 out
->msg_lspid
= in
->msg_lspid
;
180 out
->msg_qbytes
= in
->msg_qbytes
;
181 out
->msg_cbytes
= in
->msg_cbytes
; /* for ipcs */
182 out
->msg_qnum
= in
->msg_qnum
;
183 out
->msg_perm
= in
->msg_perm
;
186 /* This routine assumes the system is locked prior to calling this routine */
188 msginit(__unused
void *dummy
)
190 static int initted
= 0;
193 /* Lazy initialization on first system call; we don't have SYSINIT(). */
198 * msginfo.msgssz should be a power of two for efficiency reasons.
199 * It is also pretty silly if msginfo.msgssz is less than 8
200 * or greater than about 256 so ...
203 while (i
< 1024 && i
!= msginfo
.msgssz
)
205 if (i
!= msginfo
.msgssz
) {
206 printf("msginfo.msgssz=%d (0x%x) not a small power of 2; resetting to %d\n", msginfo
.msgssz
, msginfo
.msgssz
, MSGSSZ
);
207 msginfo
.msgssz
= MSGSSZ
;
210 if (msginfo
.msgseg
> 32767) {
211 printf("msginfo.msgseg=%d (> 32767); resetting to %d\n", msginfo
.msgseg
, MSGSEG
);
212 msginfo
.msgseg
= MSGSEG
;
217 * Allocate memory for message pool, maps, headers, and queue IDs;
218 * if this fails, fail safely and leave it uninitialized (related
219 * system calls will fail).
221 msgpool
= (char *)_MALLOC(msginfo
.msgmax
, M_SHM
, M_WAITOK
);
222 if (msgpool
== NULL
) {
223 printf("msginit: can't allocate msgpool");
226 MALLOC(msgmaps
, struct msgmap
*,
227 sizeof(struct msgmap
) * msginfo
.msgseg
,
229 if (msgmaps
== NULL
) {
230 printf("msginit: can't allocate msgmaps");
234 MALLOC(msghdrs
, struct msg
*,
235 sizeof(struct msg
) * msginfo
.msgtql
,
237 if (msghdrs
== NULL
) {
238 printf("msginit: can't allocate msghdrs");
242 MALLOC(msqids
, struct msqid_kernel
*,
243 sizeof(struct user_msqid_ds
) * msginfo
.msgmni
,
245 if (msqids
== NULL
) {
246 printf("msginit: can't allocate msqids");
252 for (i
= 0; i
< msginfo
.msgseg
; i
++) {
254 msgmaps
[i
-1].next
= i
;
255 msgmaps
[i
].next
= -1; /* implies entry is available */
258 nfree_msgmaps
= msginfo
.msgseg
;
262 for (i
= 0; i
< msginfo
.msgtql
; i
++) {
263 msghdrs
[i
].msg_type
= 0;
265 msghdrs
[i
-1].msg_next
= &msghdrs
[i
];
266 msghdrs
[i
].msg_next
= NULL
;
268 mac_sysvmsg_label_init(&msghdrs
[i
]);
271 free_msghdrs
= &msghdrs
[0];
274 for (i
= 0; i
< msginfo
.msgmni
; i
++) {
275 msqids
[i
].u
.msg_qbytes
= 0; /* implies entry is available */
276 msqids
[i
].u
.msg_perm
._seq
= 0; /* reset to a known value */
277 msqids
[i
].u
.msg_perm
.mode
= 0;
279 mac_sysvmsq_label_init(&msqids
[i
]);
287 _FREE(msgpool
, M_SHM
);
289 FREE(msgmaps
, M_SHM
);
291 FREE(msghdrs
, M_SHM
);
299 * Entry point for all MSG calls
301 /* XXX actually varargs. */
303 msgsys(struct proc
*p
, struct msgsys_args
*uap
, register_t
*retval
)
305 if (uap
->which
>= sizeof(msgcalls
)/sizeof(msgcalls
[0]))
307 return ((*msgcalls
[uap
->which
])(p
, &uap
->a2
, retval
));
311 msg_freehdr(struct msg
*msghdr
)
313 while (msghdr
->msg_ts
> 0) {
315 if (msghdr
->msg_spot
< 0 || msghdr
->msg_spot
>= msginfo
.msgseg
)
316 panic("msghdr->msg_spot out of range");
317 next
= msgmaps
[msghdr
->msg_spot
].next
;
318 msgmaps
[msghdr
->msg_spot
].next
= free_msgmaps
;
319 free_msgmaps
= msghdr
->msg_spot
;
321 msghdr
->msg_spot
= next
;
322 if (msghdr
->msg_ts
>= msginfo
.msgssz
)
323 msghdr
->msg_ts
-= msginfo
.msgssz
;
327 if (msghdr
->msg_spot
!= -1)
328 panic("msghdr->msg_spot != -1");
329 msghdr
->msg_next
= free_msghdrs
;
330 free_msghdrs
= msghdr
;
332 mac_sysvmsg_label_recycle(msghdr
);
335 * Notify waiters that there are free message headers and segments
338 wakeup((caddr_t
)&free_msghdrs
);
342 msgctl(struct proc
*p
, struct msgctl_args
*uap
, register_t
*retval
)
344 int msqid
= uap
->msqid
;
346 kauth_cred_t cred
= kauth_cred_get();
348 struct user_msqid_ds msqbuf
;
349 struct msqid_kernel
*msqptr
;
350 struct user_msqid_ds umsds
;
352 SYSV_MSG_SUBSYS_LOCK();
360 printf("call to msgctl(%d, %d, 0x%qx)\n", msqid
, cmd
, uap
->buf
);
363 AUDIT_ARG(svipc_cmd
, cmd
);
364 AUDIT_ARG(svipc_id
, msqid
);
365 msqid
= IPCID_TO_IX(msqid
);
367 if (msqid
< 0 || msqid
>= msginfo
.msgmni
) {
369 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid
,
376 msqptr
= &msqids
[msqid
];
378 if (msqptr
->u
.msg_qbytes
== 0) {
380 printf("no such msqid\n");
385 if (msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
387 printf("wrong sequence number\n");
393 eval
= mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr
, cmd
);
406 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, IPC_M
)))
410 * Check that the thread has MAC access permissions to
411 * individual msghdrs. Note: We need to do this in a
412 * separate loop because the actual loop alters the
413 * msq/msghdr info as it progresses, and there is no going
414 * back if half the way through we discover that the
415 * thread cannot free a certain msghdr. The msq will get
416 * into an inconsistent state.
418 for (msghdr
= msqptr
->u
.msg_first
; msghdr
!= NULL
;
419 msghdr
= msghdr
->msg_next
) {
420 eval
= mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr
);
425 /* Free the message headers */
426 msghdr
= msqptr
->u
.msg_first
;
427 while (msghdr
!= NULL
) {
428 struct msg
*msghdr_tmp
;
430 /* Free the segments of each message */
431 msqptr
->u
.msg_cbytes
-= msghdr
->msg_ts
;
432 msqptr
->u
.msg_qnum
--;
434 msghdr
= msghdr
->msg_next
;
435 msg_freehdr(msghdr_tmp
);
438 if (msqptr
->u
.msg_cbytes
!= 0)
439 panic("msg_cbytes is messed up");
440 if (msqptr
->u
.msg_qnum
!= 0)
441 panic("msg_qnum is messed up");
443 msqptr
->u
.msg_qbytes
= 0; /* Mark it as free */
445 mac_sysvmsq_label_recycle(msqptr
);
448 wakeup((caddr_t
)msqptr
);
454 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, IPC_M
)))
457 SYSV_MSG_SUBSYS_UNLOCK();
459 if (IS_64BIT_PROCESS(p
)) {
460 eval
= copyin(uap
->buf
, &msqbuf
, sizeof(struct user_msqid_ds
));
462 eval
= copyin(uap
->buf
, &msqbuf
, sizeof(struct msqid_ds
));
463 /* convert in place; ugly, but safe */
464 msqid_ds_32to64((struct msqid_ds
*)&msqbuf
, &msqbuf
);
469 SYSV_MSG_SUBSYS_LOCK();
471 if (msqbuf
.msg_qbytes
> msqptr
->u
.msg_qbytes
) {
472 eval
= suser(cred
, &p
->p_acflag
);
478 /* compare (msglen_t) value against restrict (int) value */
479 if (msqbuf
.msg_qbytes
> (msglen_t
)msginfo
.msgmnb
) {
481 printf("can't increase msg_qbytes beyond %d (truncating)\n",
484 msqbuf
.msg_qbytes
= msginfo
.msgmnb
; /* silently restrict qbytes to system limit */
486 if (msqbuf
.msg_qbytes
== 0) {
488 printf("can't reduce msg_qbytes to 0\n");
493 msqptr
->u
.msg_perm
.uid
= msqbuf
.msg_perm
.uid
; /* change the owner */
494 msqptr
->u
.msg_perm
.gid
= msqbuf
.msg_perm
.gid
; /* change the owner */
495 msqptr
->u
.msg_perm
.mode
= (msqptr
->u
.msg_perm
.mode
& ~0777) |
496 (msqbuf
.msg_perm
.mode
& 0777);
497 msqptr
->u
.msg_qbytes
= msqbuf
.msg_qbytes
;
498 msqptr
->u
.msg_ctime
= sysv_msgtime();
502 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, IPC_R
))) {
504 printf("requester doesn't have read access\n");
509 bcopy(msqptr
, &umsds
, sizeof(struct user_msqid_ds
));
511 SYSV_MSG_SUBSYS_UNLOCK();
512 if (IS_64BIT_PROCESS(p
)) {
513 eval
= copyout(&umsds
, uap
->buf
, sizeof(struct user_msqid_ds
));
515 struct msqid_ds msqid_ds32
;
516 msqid_ds_64to32(&umsds
, &msqid_ds32
);
517 eval
= copyout(&msqid_ds32
, uap
->buf
, sizeof(struct msqid_ds
));
519 SYSV_MSG_SUBSYS_LOCK();
524 printf("invalid command %d\n", cmd
);
533 SYSV_MSG_SUBSYS_UNLOCK();
538 msgget(__unused
struct proc
*p
, struct msgget_args
*uap
, register_t
*retval
)
542 int msgflg
= uap
->msgflg
;
543 kauth_cred_t cred
= kauth_cred_get();
544 struct msqid_kernel
*msqptr
= NULL
;
546 SYSV_MSG_SUBSYS_LOCK();
554 printf("msgget(0x%x, 0%o)\n", key
, msgflg
);
557 if (key
!= IPC_PRIVATE
) {
558 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
559 msqptr
= &msqids
[msqid
];
560 if (msqptr
->u
.msg_qbytes
!= 0 &&
561 msqptr
->u
.msg_perm
._key
== key
)
564 if (msqid
< msginfo
.msgmni
) {
566 printf("found public key\n");
568 if ((msgflg
& IPC_CREAT
) && (msgflg
& IPC_EXCL
)) {
570 printf("not exclusive\n");
575 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, msgflg
& 0700 ))) {
577 printf("requester doesn't have 0%o access\n",
583 eval
= mac_sysvmsq_check_msqget(cred
, msqptr
);
592 printf("need to allocate the user_msqid_ds\n");
594 if (key
== IPC_PRIVATE
|| (msgflg
& IPC_CREAT
)) {
595 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
597 * Look for an unallocated and unlocked user_msqid_ds.
598 * user_msqid_ds's can be locked by msgsnd or msgrcv
599 * while they are copying the message in/out. We
600 * can't re-use the entry until they release it.
602 msqptr
= &msqids
[msqid
];
603 if (msqptr
->u
.msg_qbytes
== 0 &&
604 (msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) == 0)
607 if (msqid
== msginfo
.msgmni
) {
609 printf("no more user_msqid_ds's available\n");
615 printf("msqid %d is available\n", msqid
);
617 msqptr
->u
.msg_perm
._key
= key
;
618 msqptr
->u
.msg_perm
.cuid
= kauth_cred_getuid(cred
);
619 msqptr
->u
.msg_perm
.uid
= kauth_cred_getuid(cred
);
620 msqptr
->u
.msg_perm
.cgid
= cred
->cr_gid
;
621 msqptr
->u
.msg_perm
.gid
= cred
->cr_gid
;
622 msqptr
->u
.msg_perm
.mode
= (msgflg
& 0777);
623 /* Make sure that the returned msqid is unique */
624 msqptr
->u
.msg_perm
._seq
++;
625 msqptr
->u
.msg_first
= NULL
;
626 msqptr
->u
.msg_last
= NULL
;
627 msqptr
->u
.msg_cbytes
= 0;
628 msqptr
->u
.msg_qnum
= 0;
629 msqptr
->u
.msg_qbytes
= msginfo
.msgmnb
;
630 msqptr
->u
.msg_lspid
= 0;
631 msqptr
->u
.msg_lrpid
= 0;
632 msqptr
->u
.msg_stime
= 0;
633 msqptr
->u
.msg_rtime
= 0;
634 msqptr
->u
.msg_ctime
= sysv_msgtime();
636 mac_sysvmsq_label_associate(cred
, msqptr
);
640 printf("didn't find it and wasn't asked to create it\n");
647 /* Construct the unique msqid */
648 *retval
= IXSEQ_TO_IPCID(msqid
, msqptr
->u
.msg_perm
);
649 AUDIT_ARG(svipc_id
, *retval
);
652 SYSV_MSG_SUBSYS_UNLOCK();
658 msgsnd(struct proc
*p
, struct msgsnd_args
*uap
, register_t
*retval
)
660 __pthread_testcancel(1);
661 return(msgsnd_nocancel(p
, (struct msgsnd_nocancel_args
*)uap
, retval
));
665 msgsnd_nocancel(struct proc
*p
, struct msgsnd_nocancel_args
*uap
, register_t
*retval
)
667 int msqid
= uap
->msqid
;
668 user_addr_t user_msgp
= uap
->msgp
;
669 size_t msgsz
= (size_t)uap
->msgsz
; /* limit to 4G */
670 int msgflg
= uap
->msgflg
;
671 int segs_needed
, eval
;
672 struct msqid_kernel
*msqptr
;
678 SYSV_MSG_SUBSYS_LOCK();
686 printf("call to msgsnd(%d, 0x%qx, %d, %d)\n", msqid
, user_msgp
, msgsz
,
690 AUDIT_ARG(svipc_id
, msqid
);
691 msqid
= IPCID_TO_IX(msqid
);
693 if (msqid
< 0 || msqid
>= msginfo
.msgmni
) {
695 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid
,
702 msqptr
= &msqids
[msqid
];
703 if (msqptr
->u
.msg_qbytes
== 0) {
705 printf("no such message queue id\n");
710 if (msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
712 printf("wrong sequence number\n");
718 if ((eval
= ipcperm(kauth_cred_get(), &msqptr
->u
.msg_perm
, IPC_W
))) {
720 printf("requester doesn't have write access\n");
726 eval
= mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr
);
730 segs_needed
= (msgsz
+ msginfo
.msgssz
- 1) / msginfo
.msgssz
;
732 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz
, msginfo
.msgssz
,
737 * If we suffer resource starvation, we will sleep in this loop and
738 * wait for more resources to become available. This is a loop to
739 * ensure reacquisition of the mutex following any sleep, since there
740 * are multiple resources under contention.
743 void *blocking_resource
= NULL
;
746 * Check that we have not had the maximum message size change
747 * out from under us and render our message invalid while we
748 * slept waiting for some resource.
750 if (msgsz
> msqptr
->u
.msg_qbytes
) {
752 printf("msgsz > msqptr->msg_qbytes\n");
759 * If the user_msqid_ds is already locked, we need to sleep on
760 * the queue until it's unlocked.
762 if (msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) {
764 printf("msqid is locked\n");
766 blocking_resource
= msqptr
;
770 * If our message plus the messages already in the queue would
771 * cause us to exceed the maximum number of bytes wer are
772 * permitted to queue, then block on the queue until it drains.
774 if (msgsz
+ msqptr
->u
.msg_cbytes
> msqptr
->u
.msg_qbytes
) {
776 printf("msgsz + msg_cbytes > msg_qbytes\n");
778 blocking_resource
= msqptr
;
782 * Both message maps and message headers are protected by
783 * sleeping on the address of the pointer to the list of free
784 * message headers, since they are allocated and freed in
787 if (segs_needed
> nfree_msgmaps
) {
789 printf("segs_needed > nfree_msgmaps\n");
791 blocking_resource
= &free_msghdrs
;
793 if (free_msghdrs
== NULL
) {
795 printf("no more msghdrs\n");
797 blocking_resource
= &free_msghdrs
;
800 if (blocking_resource
!= NULL
) {
803 if ((msgflg
& IPC_NOWAIT
) != 0) {
805 printf("need more resources but caller doesn't want to wait\n");
811 if ((msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0) {
813 printf("we don't own the user_msqid_ds\n");
817 /* Force later arrivals to wait for our
820 printf("we own the user_msqid_ds\n");
822 msqptr
->u
.msg_perm
.mode
|= MSG_LOCKED
;
826 printf("goodnight\n");
828 eval
= msleep(blocking_resource
, &sysv_msg_subsys_mutex
, (PZERO
- 4) | PCATCH
,
831 printf("good morning, eval=%d\n", eval
);
834 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
837 printf("msgsnd: interrupted system call\n");
844 * Make sure that the msq queue still exists
847 if (msqptr
->u
.msg_qbytes
== 0) {
849 printf("msqid deleted\n");
858 printf("got all the resources that we need\n");
865 * We have the resources that we need.
869 if (msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
)
870 panic("msg_perm.mode & MSG_LOCKED");
871 if (segs_needed
> nfree_msgmaps
)
872 panic("segs_needed > nfree_msgmaps");
873 if (msgsz
+ msqptr
->u
.msg_cbytes
> msqptr
->u
.msg_qbytes
)
874 panic("msgsz + msg_cbytes > msg_qbytes");
875 if (free_msghdrs
== NULL
)
876 panic("no more msghdrs");
879 * Re-lock the user_msqid_ds in case we page-fault when copying in
882 if ((msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0)
883 panic("user_msqid_ds is already locked");
884 msqptr
->u
.msg_perm
.mode
|= MSG_LOCKED
;
887 * Allocate a message header
889 msghdr
= free_msghdrs
;
890 free_msghdrs
= msghdr
->msg_next
;
891 msghdr
->msg_spot
= -1;
892 msghdr
->msg_ts
= msgsz
;
895 mac_sysvmsg_label_associate(kauth_cred_get(), msqptr
, msghdr
);
898 * Allocate space for the message
901 while (segs_needed
> 0) {
902 if (nfree_msgmaps
<= 0)
903 panic("not enough msgmaps");
904 if (free_msgmaps
== -1)
905 panic("nil free_msgmaps");
908 panic("next too low #1");
909 if (next
>= msginfo
.msgseg
)
910 panic("next out of range #1");
912 printf("allocating segment %d to message\n", next
);
914 free_msgmaps
= msgmaps
[next
].next
;
916 msgmaps
[next
].next
= msghdr
->msg_spot
;
917 msghdr
->msg_spot
= next
;
922 * Copy in the message type. For a 64 bit process, this is 64 bits,
923 * but we only ever use the low 32 bits, so the cast is OK.
925 if (IS_64BIT_PROCESS(p
)) {
926 SYSV_MSG_SUBSYS_UNLOCK();
927 eval
= copyin(user_msgp
, &msgtype
, sizeof(msgtype
));
928 SYSV_MSG_SUBSYS_LOCK();
929 msghdr
->msg_type
= CAST_DOWN(long,msgtype
);
930 user_msgp
= user_msgp
+ sizeof(msgtype
); /* ptr math */
932 SYSV_MSG_SUBSYS_UNLOCK();
933 eval
= copyin(user_msgp
, &msghdr
->msg_type
, sizeof(long));
934 SYSV_MSG_SUBSYS_LOCK();
935 user_msgp
= user_msgp
+ sizeof(long); /* ptr math */
940 printf("error %d copying the message type\n", eval
);
943 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
944 wakeup((caddr_t
)msqptr
);
950 * Validate the message type
952 if (msghdr
->msg_type
< 1) {
954 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
955 wakeup((caddr_t
)msqptr
);
957 printf("mtype (%d) < 1\n", msghdr
->msg_type
);
964 * Copy in the message body
966 next
= msghdr
->msg_spot
;
969 /* compare input (size_t) value against restrict (int) value */
970 if (msgsz
> (size_t)msginfo
.msgssz
)
971 tlen
= msginfo
.msgssz
;
975 panic("next too low #2");
976 if (next
>= msginfo
.msgseg
)
977 panic("next out of range #2");
979 SYSV_MSG_SUBSYS_UNLOCK();
980 eval
= copyin(user_msgp
, &msgpool
[next
* msginfo
.msgssz
], tlen
);
981 SYSV_MSG_SUBSYS_LOCK();
985 printf("error %d copying in message segment\n", eval
);
988 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
989 wakeup((caddr_t
)msqptr
);
994 user_msgp
= user_msgp
+ tlen
; /* ptr math */
995 next
= msgmaps
[next
].next
;
998 panic("didn't use all the msg segments");
1001 * We've got the message. Unlock the user_msqid_ds.
1004 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
1007 * Make sure that the user_msqid_ds is still allocated.
1010 if (msqptr
->u
.msg_qbytes
== 0) {
1011 msg_freehdr(msghdr
);
1012 wakeup((caddr_t
)msqptr
);
1013 /* The SVID says to return EIDRM. */
1017 /* Unfortunately, BSD doesn't define that code yet! */
1025 * Note: Since the task/thread allocates the msghdr and usually
1026 * primes it with its own MAC label, for a majority of policies, it
1027 * won't be necessary to check whether the msghdr has access
1028 * permissions to the msgq. The mac_sysvmsq_check_msqsnd check would
1029 * suffice in that case. However, this hook may be required where
1030 * individual policies derive a non-identical label for the msghdr
1031 * from the current thread label and may want to check the msghdr
1032 * enqueue permissions, along with read/write permissions to the
1035 eval
= mac_sysvmsq_check_enqueue(kauth_cred_get(), msghdr
, msqptr
);
1037 msg_freehdr(msghdr
);
1038 wakeup((caddr_t
) msqptr
);
1043 * Put the message into the queue
1046 if (msqptr
->u
.msg_first
== NULL
) {
1047 msqptr
->u
.msg_first
= msghdr
;
1048 msqptr
->u
.msg_last
= msghdr
;
1050 msqptr
->u
.msg_last
->msg_next
= msghdr
;
1051 msqptr
->u
.msg_last
= msghdr
;
1053 msqptr
->u
.msg_last
->msg_next
= NULL
;
1055 msqptr
->u
.msg_cbytes
+= msghdr
->msg_ts
;
1056 msqptr
->u
.msg_qnum
++;
1057 msqptr
->u
.msg_lspid
= p
->p_pid
;
1058 msqptr
->u
.msg_stime
= sysv_msgtime();
1060 wakeup((caddr_t
)msqptr
);
1065 SYSV_MSG_SUBSYS_UNLOCK();
1071 msgrcv(struct proc
*p
, struct msgrcv_args
*uap
, user_ssize_t
*retval
)
1073 __pthread_testcancel(1);
1074 return(msgrcv_nocancel(p
, (struct msgrcv_nocancel_args
*)uap
, retval
));
1078 msgrcv_nocancel(struct proc
*p
, struct msgrcv_nocancel_args
*uap
, user_ssize_t
*retval
)
1080 int msqid
= uap
->msqid
;
1081 user_addr_t user_msgp
= uap
->msgp
;
1082 size_t msgsz
= (size_t)uap
->msgsz
; /* limit to 4G */
1083 long msgtyp
= (long)uap
->msgtyp
; /* limit to 32 bits */
1084 int msgflg
= uap
->msgflg
;
1086 struct msqid_kernel
*msqptr
;
1090 user_long_t msgtype
;
1093 SYSV_MSG_SUBSYS_LOCK();
1101 printf("call to msgrcv(%d, 0x%qx, %d, %ld, %d)\n", msqid
, user_msgp
,
1102 msgsz
, msgtyp
, msgflg
);
1105 AUDIT_ARG(svipc_id
, msqid
);
1106 msqid
= IPCID_TO_IX(msqid
);
1108 if (msqid
< 0 || msqid
>= msginfo
.msgmni
) {
1110 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid
,
1117 msqptr
= &msqids
[msqid
];
1118 if (msqptr
->u
.msg_qbytes
== 0) {
1120 printf("no such message queue id\n");
1125 if (msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
1127 printf("wrong sequence number\n");
1133 if ((eval
= ipcperm(kauth_cred_get(), &msqptr
->u
.msg_perm
, IPC_R
))) {
1135 printf("requester doesn't have read access\n");
1141 eval
= mac_sysvmsq_check_msqrcv(kauth_cred_get(), msqptr
);
1146 while (msghdr
== NULL
) {
1148 msghdr
= msqptr
->u
.msg_first
;
1149 if (msghdr
!= NULL
) {
1150 if (msgsz
< msghdr
->msg_ts
&&
1151 (msgflg
& MSG_NOERROR
) == 0) {
1153 printf("first message on the queue is too big (want %d, got %d)\n",
1154 msgsz
, msghdr
->msg_ts
);
1160 eval
= mac_sysvmsq_check_msgrcv(kauth_cred_get(),
1165 if (msqptr
->u
.msg_first
== msqptr
->u
.msg_last
) {
1166 msqptr
->u
.msg_first
= NULL
;
1167 msqptr
->u
.msg_last
= NULL
;
1169 msqptr
->u
.msg_first
= msghdr
->msg_next
;
1170 if (msqptr
->u
.msg_first
== NULL
)
1171 panic("msg_first/last messed up #1");
1175 struct msg
*previous
;
1179 prev
= &(msqptr
->u
.msg_first
);
1180 while ((msghdr
= *prev
) != NULL
) {
1182 * Is this message's type an exact match or is
1183 * this message's type less than or equal to
1184 * the absolute value of a negative msgtyp?
1185 * Note that the second half of this test can
1186 * NEVER be true if msgtyp is positive since
1187 * msg_type is always positive!
1190 if (msgtyp
== msghdr
->msg_type
||
1191 msghdr
->msg_type
<= -msgtyp
) {
1193 printf("found message type %d, requested %d\n",
1194 msghdr
->msg_type
, msgtyp
);
1196 if (msgsz
< msghdr
->msg_ts
&&
1197 (msgflg
& MSG_NOERROR
) == 0) {
1199 printf("requested message on the queue is too big (want %d, got %d)\n",
1200 msgsz
, msghdr
->msg_ts
);
1206 eval
= mac_sysvmsq_check_msgrcv(
1207 kauth_cred_get(), msghdr
);
1211 *prev
= msghdr
->msg_next
;
1212 if (msghdr
== msqptr
->u
.msg_last
) {
1213 if (previous
== NULL
) {
1215 &msqptr
->u
.msg_first
)
1216 panic("msg_first/last messed up #2");
1217 msqptr
->u
.msg_first
=
1219 msqptr
->u
.msg_last
=
1223 &msqptr
->u
.msg_first
)
1224 panic("msg_first/last messed up #3");
1225 msqptr
->u
.msg_last
=
1232 prev
= &(msghdr
->msg_next
);
1237 * We've either extracted the msghdr for the appropriate
1238 * message or there isn't one.
1239 * If there is one then bail out of this loop.
1246 * Hmph! No message found. Does the user want to wait?
1249 if ((msgflg
& IPC_NOWAIT
) != 0) {
1251 printf("no appropriate message found (msgtyp=%d)\n",
1254 /* The SVID says to return ENOMSG. */
1258 /* Unfortunately, BSD doesn't define that code yet! */
1265 * Wait for something to happen
1269 printf("msgrcv: goodnight\n");
1271 eval
= msleep((caddr_t
)msqptr
, &sysv_msg_subsys_mutex
, (PZERO
- 4) | PCATCH
, "msgwait",
1274 printf("msgrcv: good morning (eval=%d)\n", eval
);
1279 printf("msgsnd: interrupted system call\n");
1286 * Make sure that the msq queue still exists
1289 if (msqptr
->u
.msg_qbytes
== 0 ||
1290 msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
1292 printf("msqid deleted\n");
1294 /* The SVID says to return EIDRM. */
1298 /* Unfortunately, BSD doesn't define that code yet! */
1306 * Return the message to the user.
1308 * First, do the bookkeeping (before we risk being interrupted).
1311 msqptr
->u
.msg_cbytes
-= msghdr
->msg_ts
;
1312 msqptr
->u
.msg_qnum
--;
1313 msqptr
->u
.msg_lrpid
= p
->p_pid
;
1314 msqptr
->u
.msg_rtime
= sysv_msgtime();
1317 * Make msgsz the actual amount that we'll be returning.
1318 * Note that this effectively truncates the message if it is too long
1319 * (since msgsz is never increased).
1323 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz
,
1326 if (msgsz
> msghdr
->msg_ts
)
1327 msgsz
= msghdr
->msg_ts
;
1330 * Return the type to the user.
1334 * Copy out the message type. For a 64 bit process, this is 64 bits,
1335 * but we only ever use the low 32 bits, so the cast is OK.
1337 if (IS_64BIT_PROCESS(p
)) {
1338 msgtype
= msghdr
->msg_type
;
1339 SYSV_MSG_SUBSYS_UNLOCK();
1340 eval
= copyout(&msgtype
, user_msgp
, sizeof(msgtype
));
1341 SYSV_MSG_SUBSYS_LOCK();
1342 user_msgp
= user_msgp
+ sizeof(msgtype
); /* ptr math */
1344 msg_type_long
= msghdr
->msg_type
;
1345 SYSV_MSG_SUBSYS_UNLOCK();
1346 eval
= copyout(&msg_type_long
, user_msgp
, sizeof(long));
1347 SYSV_MSG_SUBSYS_LOCK();
1348 user_msgp
= user_msgp
+ sizeof(long); /* ptr math */
1353 printf("error (%d) copying out message type\n", eval
);
1355 msg_freehdr(msghdr
);
1356 wakeup((caddr_t
)msqptr
);
1363 * Return the segments to the user
1366 next
= msghdr
->msg_spot
;
1367 for (len
= 0; len
< msgsz
; len
+= msginfo
.msgssz
) {
1370 /* compare input (size_t) value against restrict (int) value */
1371 if (msgsz
> (size_t)msginfo
.msgssz
)
1372 tlen
= msginfo
.msgssz
;
1376 panic("next too low #3");
1377 if (next
>= msginfo
.msgseg
)
1378 panic("next out of range #3");
1379 SYSV_MSG_SUBSYS_UNLOCK();
1380 eval
= copyout(&msgpool
[next
* msginfo
.msgssz
],
1382 SYSV_MSG_SUBSYS_LOCK();
1385 printf("error (%d) copying out message segment\n",
1388 msg_freehdr(msghdr
);
1389 wakeup((caddr_t
)msqptr
);
1392 user_msgp
= user_msgp
+ tlen
; /* ptr math */
1393 next
= msgmaps
[next
].next
;
1397 * Done, return the actual number of bytes copied out.
1400 msg_freehdr(msghdr
);
1401 wakeup((caddr_t
)msqptr
);
1405 SYSV_MSG_SUBSYS_UNLOCK();
1410 IPCS_msg_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
1411 __unused
int arg2
, struct sysctl_req
*req
)
1416 struct IPCS_command u32
;
1417 struct user_IPCS_command u64
;
1419 struct msqid_ds msqid_ds32
; /* post conversion, 32 bit version */
1421 size_t ipcs_sz
= sizeof(struct user_IPCS_command
);
1422 size_t msqid_ds_sz
= sizeof(struct user_msqid_ds
);
1423 struct proc
*p
= current_proc();
1425 if (!IS_64BIT_PROCESS(p
)) {
1426 ipcs_sz
= sizeof(struct IPCS_command
);
1427 msqid_ds_sz
= sizeof(struct msqid_ds
);
1430 /* Copy in the command structure */
1431 if ((error
= SYSCTL_IN(req
, &ipcs
, ipcs_sz
)) != 0) {
1435 if (!IS_64BIT_PROCESS(p
)) /* convert in place */
1436 ipcs
.u64
.ipcs_data
= CAST_USER_ADDR_T(ipcs
.u32
.ipcs_data
);
1438 /* Let us version this interface... */
1439 if (ipcs
.u64
.ipcs_magic
!= IPCS_MAGIC
) {
1443 SYSV_MSG_SUBSYS_LOCK();
1445 switch(ipcs
.u64
.ipcs_op
) {
1446 case IPCS_MSG_CONF
: /* Obtain global configuration data */
1447 if (ipcs
.u64
.ipcs_datalen
!= sizeof(struct msginfo
)) {
1451 if (ipcs
.u64
.ipcs_cursor
!= 0) { /* fwd. compat. */
1455 SYSV_MSG_SUBSYS_UNLOCK();
1456 error
= copyout(&msginfo
, ipcs
.u64
.ipcs_data
, ipcs
.u64
.ipcs_datalen
);
1457 SYSV_MSG_SUBSYS_LOCK();
1460 case IPCS_MSG_ITER
: /* Iterate over existing segments */
1461 /* Not done up top so we can set limits via sysctl (later) */
1467 cursor
= ipcs
.u64
.ipcs_cursor
;
1468 if (cursor
< 0 || cursor
>= msginfo
.msgmni
) {
1472 if (ipcs
.u64
.ipcs_datalen
!= (int)msqid_ds_sz
) {
1476 for( ; cursor
< msginfo
.msgmni
; cursor
++) {
1477 if (msqids
[cursor
].u
.msg_qbytes
!= 0) /* allocated */
1481 if (cursor
== msginfo
.msgmni
) {
1486 msqid_dsp
= &msqids
[cursor
]; /* default: 64 bit */
1489 * If necessary, convert the 64 bit kernel segment
1490 * descriptor to a 32 bit user one.
1492 if (!IS_64BIT_PROCESS(p
)) {
1493 msqid_ds_64to32(msqid_dsp
, &msqid_ds32
);
1494 msqid_dsp
= &msqid_ds32
;
1496 SYSV_MSG_SUBSYS_UNLOCK();
1497 error
= copyout(msqid_dsp
, ipcs
.u64
.ipcs_data
, ipcs
.u64
.ipcs_datalen
);
1500 ipcs
.u64
.ipcs_cursor
= cursor
+ 1;
1502 if (!IS_64BIT_PROCESS(p
)) /* convert in place */
1503 ipcs
.u32
.ipcs_data
= CAST_DOWN(void *,ipcs
.u64
.ipcs_data
);
1504 error
= SYSCTL_OUT(req
, &ipcs
, ipcs_sz
);
1506 SYSV_MSG_SUBSYS_LOCK();
1514 SYSV_MSG_SUBSYS_UNLOCK();
1518 SYSCTL_DECL(_kern_sysv_ipcs
);
1519 SYSCTL_PROC(_kern_sysv_ipcs
, OID_AUTO
, msg
, CTLFLAG_RW
|CTLFLAG_ANYBODY
,
1520 0, 0, IPCS_msg_sysctl
,
1521 "S,IPCS_msg_command",
1522 "ipcs msg command interface");
1524 #endif /* SYSV_MSG */