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 <security/audit/audit.h>
64 #include <sys/filedesc.h>
65 #include <sys/file_internal.h>
66 #include <sys/sysctl.h>
67 #include <sys/sysproto.h>
71 #include <security/mac_framework.h>
76 static int msginit(void *);
81 /* Uncomment this line to see MAC debugging output. */
82 /* #define MAC_DEBUG */
84 #define MPRINTF(a) printf(a)
88 static void msg_freehdr(struct msg
*msghdr
);
90 typedef int sy_call_t(struct proc
*, void *, int *);
92 /* XXX casting to (sy_call_t *) is bogus, as usual. */
93 static sy_call_t
*msgcalls
[] = {
94 (sy_call_t
*)msgctl
, (sy_call_t
*)msgget
,
95 (sy_call_t
*)msgsnd
, (sy_call_t
*)msgrcv
98 static int nfree_msgmaps
; /* # of free map entries */
99 static short free_msgmaps
; /* free map entries list head */
100 static struct msg
*free_msghdrs
; /* list of free msg headers */
101 char *msgpool
; /* MSGMAX byte long msg buffer pool */
102 struct msgmap
*msgmaps
; /* MSGSEG msgmap structures */
103 struct msg
*msghdrs
; /* MSGTQL msg headers */
104 struct msqid_kernel
*msqids
; /* MSGMNI msqid_kernel structs (wrapping user_msqid_ds structs) */
106 static lck_grp_t
*sysv_msg_subsys_lck_grp
;
107 static lck_grp_attr_t
*sysv_msg_subsys_lck_grp_attr
;
108 static lck_attr_t
*sysv_msg_subsys_lck_attr
;
109 static lck_mtx_t sysv_msg_subsys_mutex
;
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)
114 void sysv_msg_lock_init(void);
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 */
124 struct 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 */
132 #endif /* __APPLE_API_PRIVATE */
134 /* Initialize the mutex governing access to the SysV msg subsystem */
135 __private_extern__
void
136 sysv_msg_lock_init( void )
138 sysv_msg_subsys_lck_grp_attr
= lck_grp_attr_alloc_init();
140 sysv_msg_subsys_lck_grp
= lck_grp_alloc_init("sysv_msg_subsys_lock", sysv_msg_subsys_lck_grp_attr
);
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
);
146 static __inline__ user_time_t
155 * NOTE: Source and target may *NOT* overlap! (target is smaller)
158 msqid_ds_kerneltouser32(struct user_msqid_ds
*in
, struct user32_msqid_ds
*out
)
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 */
172 msqid_ds_kerneltouser64(struct user_msqid_ds
*in
, struct user64_msqid_ds
*out
)
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 */
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
191 msqid_ds_user32tokernel(struct user32_msqid_ds
*in
, struct user_msqid_ds
*out
)
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
;
205 msqid_ds_user64tokernel(struct user64_msqid_ds
*in
, struct user_msqid_ds
*out
)
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
;
218 /* This routine assumes the system is locked prior to calling this routine */
220 msginit(__unused
void *dummy
)
222 static int initted
= 0;
225 /* Lazy initialization on first system call; we don't have SYSINIT(). */
231 * msginfo.msgssz should be a power of two for efficiency reasons.
232 * It is also pretty silly if msginfo.msgssz is less than 8
233 * or greater than about 256 so ...
236 while (i
< 1024 && i
!= msginfo
.msgssz
) {
239 if (i
!= msginfo
.msgssz
) {
240 printf("msginfo.msgssz=%d (0x%x) not a small power of 2; resetting to %d\n", msginfo
.msgssz
, msginfo
.msgssz
, MSGSSZ
);
241 msginfo
.msgssz
= MSGSSZ
;
244 if (msginfo
.msgseg
> 32767) {
245 printf("msginfo.msgseg=%d (> 32767); resetting to %d\n", msginfo
.msgseg
, MSGSEG
);
246 msginfo
.msgseg
= MSGSEG
;
251 * Allocate memory for message pool, maps, headers, and queue IDs;
252 * if this fails, fail safely and leave it uninitialized (related
253 * system calls will fail).
255 msgpool
= (char *)_MALLOC(msginfo
.msgmax
, M_SHM
, M_WAITOK
);
256 if (msgpool
== NULL
) {
257 printf("msginit: can't allocate msgpool");
260 MALLOC(msgmaps
, struct msgmap
*,
261 sizeof(struct msgmap
) * msginfo
.msgseg
,
263 if (msgmaps
== NULL
) {
264 printf("msginit: can't allocate msgmaps");
268 MALLOC(msghdrs
, struct msg
*,
269 sizeof(struct msg
) * msginfo
.msgtql
,
271 if (msghdrs
== NULL
) {
272 printf("msginit: can't allocate msghdrs");
276 MALLOC(msqids
, struct msqid_kernel
*,
277 sizeof(struct msqid_kernel
) * msginfo
.msgmni
,
279 if (msqids
== NULL
) {
280 printf("msginit: can't allocate msqids");
286 for (i
= 0; i
< msginfo
.msgseg
; i
++) {
288 msgmaps
[i
- 1].next
= i
;
290 msgmaps
[i
].next
= -1; /* implies entry is available */
293 nfree_msgmaps
= msginfo
.msgseg
;
297 for (i
= 0; i
< msginfo
.msgtql
; i
++) {
298 msghdrs
[i
].msg_type
= 0;
300 msghdrs
[i
- 1].msg_next
= &msghdrs
[i
];
302 msghdrs
[i
].msg_next
= NULL
;
304 mac_sysvmsg_label_init(&msghdrs
[i
]);
307 free_msghdrs
= &msghdrs
[0];
310 for (i
= 0; i
< msginfo
.msgmni
; i
++) {
311 msqids
[i
].u
.msg_qbytes
= 0; /* implies entry is available */
312 msqids
[i
].u
.msg_perm
._seq
= 0; /* reset to a known value */
313 msqids
[i
].u
.msg_perm
.mode
= 0;
315 mac_sysvmsq_label_init(&msqids
[i
]);
322 if (msgpool
!= NULL
) {
323 _FREE(msgpool
, M_SHM
);
325 if (msgmaps
!= NULL
) {
326 FREE(msgmaps
, M_SHM
);
328 if (msghdrs
!= NULL
) {
329 FREE(msghdrs
, M_SHM
);
331 if (msqids
!= NULL
) {
341 * Entry point for all MSG calls: msgctl, msgget, msgsnd, msgrcv
343 * Parameters: p Process requesting the call
344 * uap User argument descriptor (see below)
345 * retval Return value of the selected msg call
347 * Indirect parameters: uap->which msg call to invoke (index in array of msg calls)
348 * uap->a2 User argument descriptor
353 * Implicit returns: retval Return value of the selected msg call
355 * DEPRECATED: This interface should not be used to call the other MSG
356 * functions (msgctl, msgget, msgsnd, msgrcv). The correct
357 * usage is to call the other MSG functions directly.
361 msgsys(struct proc
*p
, struct msgsys_args
*uap
, int32_t *retval
)
363 if (uap
->which
>= sizeof(msgcalls
) / sizeof(msgcalls
[0])) {
366 return (*msgcalls
[uap
->which
])(p
, &uap
->a2
, retval
);
370 msg_freehdr(struct msg
*msghdr
)
372 while (msghdr
->msg_ts
> 0) {
374 if (msghdr
->msg_spot
< 0 || msghdr
->msg_spot
>= msginfo
.msgseg
) {
375 panic("msghdr->msg_spot out of range");
377 next
= msgmaps
[msghdr
->msg_spot
].next
;
378 msgmaps
[msghdr
->msg_spot
].next
= free_msgmaps
;
379 free_msgmaps
= msghdr
->msg_spot
;
381 msghdr
->msg_spot
= next
;
382 if (msghdr
->msg_ts
>= msginfo
.msgssz
) {
383 msghdr
->msg_ts
-= msginfo
.msgssz
;
388 if (msghdr
->msg_spot
!= -1) {
389 panic("msghdr->msg_spot != -1");
391 msghdr
->msg_next
= free_msghdrs
;
392 free_msghdrs
= msghdr
;
394 mac_sysvmsg_label_recycle(msghdr
);
397 * Notify waiters that there are free message headers and segments
400 wakeup((caddr_t
)&free_msghdrs
);
404 msgctl(struct proc
*p
, struct msgctl_args
*uap
, int32_t *retval
)
406 int msqid
= uap
->msqid
;
408 kauth_cred_t cred
= kauth_cred_get();
410 struct user_msqid_ds msqbuf
;
411 struct msqid_kernel
*msqptr
;
413 SYSV_MSG_SUBSYS_LOCK();
421 printf("call to msgctl(%d, %d, 0x%qx)\n", msqid
, cmd
, uap
->buf
);
424 AUDIT_ARG(svipc_cmd
, cmd
);
425 AUDIT_ARG(svipc_id
, msqid
);
426 msqid
= IPCID_TO_IX(msqid
);
428 if (msqid
< 0 || msqid
>= msginfo
.msgmni
) {
430 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid
,
437 msqptr
= &msqids
[msqid
];
439 if (msqptr
->u
.msg_qbytes
== 0) {
441 printf("no such msqid\n");
446 if (msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
448 printf("wrong sequence number\n");
454 eval
= mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr
, cmd
);
467 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, IPC_M
))) {
472 * Check that the thread has MAC access permissions to
473 * individual msghdrs. Note: We need to do this in a
474 * separate loop because the actual loop alters the
475 * msq/msghdr info as it progresses, and there is no going
476 * back if half the way through we discover that the
477 * thread cannot free a certain msghdr. The msq will get
478 * into an inconsistent state.
480 for (msghdr
= msqptr
->u
.msg_first
; msghdr
!= NULL
;
481 msghdr
= msghdr
->msg_next
) {
482 eval
= mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr
);
488 /* Free the message headers */
489 msghdr
= msqptr
->u
.msg_first
;
490 while (msghdr
!= NULL
) {
491 struct msg
*msghdr_tmp
;
493 /* Free the segments of each message */
494 msqptr
->u
.msg_cbytes
-= msghdr
->msg_ts
;
495 msqptr
->u
.msg_qnum
--;
497 msghdr
= msghdr
->msg_next
;
498 msg_freehdr(msghdr_tmp
);
501 if (msqptr
->u
.msg_cbytes
!= 0) {
502 panic("msg_cbytes is messed up");
504 if (msqptr
->u
.msg_qnum
!= 0) {
505 panic("msg_qnum is messed up");
508 msqptr
->u
.msg_qbytes
= 0; /* Mark it as free */
510 mac_sysvmsq_label_recycle(msqptr
);
513 wakeup((caddr_t
)msqptr
);
519 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, IPC_M
))) {
523 SYSV_MSG_SUBSYS_UNLOCK();
525 if (IS_64BIT_PROCESS(p
)) {
526 struct user64_msqid_ds tmpds
;
527 eval
= copyin(uap
->buf
, &tmpds
, sizeof(tmpds
));
529 msqid_ds_user64tokernel(&tmpds
, &msqbuf
);
531 struct user32_msqid_ds tmpds
;
533 eval
= copyin(uap
->buf
, &tmpds
, sizeof(tmpds
));
535 msqid_ds_user32tokernel(&tmpds
, &msqbuf
);
541 SYSV_MSG_SUBSYS_LOCK();
543 if (msqbuf
.msg_qbytes
> msqptr
->u
.msg_qbytes
) {
544 eval
= suser(cred
, &p
->p_acflag
);
551 /* compare (msglen_t) value against restrict (int) value */
552 if (msqbuf
.msg_qbytes
> (user_msglen_t
)msginfo
.msgmnb
) {
554 printf("can't increase msg_qbytes beyond %d (truncating)\n",
557 msqbuf
.msg_qbytes
= msginfo
.msgmnb
; /* silently restrict qbytes to system limit */
559 if (msqbuf
.msg_qbytes
== 0) {
561 printf("can't reduce msg_qbytes to 0\n");
566 msqptr
->u
.msg_perm
.uid
= msqbuf
.msg_perm
.uid
; /* change the owner */
567 msqptr
->u
.msg_perm
.gid
= msqbuf
.msg_perm
.gid
; /* change the owner */
568 msqptr
->u
.msg_perm
.mode
= (msqptr
->u
.msg_perm
.mode
& ~0777) |
569 (msqbuf
.msg_perm
.mode
& 0777);
570 msqptr
->u
.msg_qbytes
= msqbuf
.msg_qbytes
;
571 msqptr
->u
.msg_ctime
= sysv_msgtime();
575 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, IPC_R
))) {
577 printf("requester doesn't have read access\n");
582 SYSV_MSG_SUBSYS_UNLOCK();
583 if (IS_64BIT_PROCESS(p
)) {
584 struct user64_msqid_ds msqid_ds64
= {};
585 msqid_ds_kerneltouser64(&msqptr
->u
, &msqid_ds64
);
586 eval
= copyout(&msqid_ds64
, uap
->buf
, sizeof(msqid_ds64
));
588 struct user32_msqid_ds msqid_ds32
= {};
589 msqid_ds_kerneltouser32(&msqptr
->u
, &msqid_ds32
);
590 eval
= copyout(&msqid_ds32
, uap
->buf
, sizeof(msqid_ds32
));
592 SYSV_MSG_SUBSYS_LOCK();
597 printf("invalid command %d\n", cmd
);
607 SYSV_MSG_SUBSYS_UNLOCK();
612 msgget(__unused
struct proc
*p
, struct msgget_args
*uap
, int32_t *retval
)
616 int msgflg
= uap
->msgflg
;
617 kauth_cred_t cred
= kauth_cred_get();
618 struct msqid_kernel
*msqptr
= NULL
;
620 SYSV_MSG_SUBSYS_LOCK();
628 printf("msgget(0x%x, 0%o)\n", key
, msgflg
);
631 if (key
!= IPC_PRIVATE
) {
632 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
633 msqptr
= &msqids
[msqid
];
634 if (msqptr
->u
.msg_qbytes
!= 0 &&
635 msqptr
->u
.msg_perm
._key
== key
) {
639 if (msqid
< msginfo
.msgmni
) {
641 printf("found public key\n");
643 if ((msgflg
& IPC_CREAT
) && (msgflg
& IPC_EXCL
)) {
645 printf("not exclusive\n");
650 if ((eval
= ipcperm(cred
, &msqptr
->u
.msg_perm
, msgflg
& 0700 ))) {
652 printf("requester doesn't have 0%o access\n",
658 eval
= mac_sysvmsq_check_msqget(cred
, msqptr
);
668 printf("need to allocate the user_msqid_ds\n");
670 if (key
== IPC_PRIVATE
|| (msgflg
& IPC_CREAT
)) {
671 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
673 * Look for an unallocated and unlocked user_msqid_ds.
674 * user_msqid_ds's can be locked by msgsnd or msgrcv
675 * while they are copying the message in/out. We
676 * can't re-use the entry until they release it.
678 msqptr
= &msqids
[msqid
];
679 if (msqptr
->u
.msg_qbytes
== 0 &&
680 (msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) == 0) {
684 if (msqid
== msginfo
.msgmni
) {
686 printf("no more user_msqid_ds's available\n");
692 printf("msqid %d is available\n", msqid
);
694 msqptr
->u
.msg_perm
._key
= key
;
695 msqptr
->u
.msg_perm
.cuid
= kauth_cred_getuid(cred
);
696 msqptr
->u
.msg_perm
.uid
= kauth_cred_getuid(cred
);
697 msqptr
->u
.msg_perm
.cgid
= kauth_cred_getgid(cred
);
698 msqptr
->u
.msg_perm
.gid
= kauth_cred_getgid(cred
);
699 msqptr
->u
.msg_perm
.mode
= (msgflg
& 0777);
700 /* Make sure that the returned msqid is unique */
701 msqptr
->u
.msg_perm
._seq
++;
702 msqptr
->u
.msg_first
= NULL
;
703 msqptr
->u
.msg_last
= NULL
;
704 msqptr
->u
.msg_cbytes
= 0;
705 msqptr
->u
.msg_qnum
= 0;
706 msqptr
->u
.msg_qbytes
= msginfo
.msgmnb
;
707 msqptr
->u
.msg_lspid
= 0;
708 msqptr
->u
.msg_lrpid
= 0;
709 msqptr
->u
.msg_stime
= 0;
710 msqptr
->u
.msg_rtime
= 0;
711 msqptr
->u
.msg_ctime
= sysv_msgtime();
713 mac_sysvmsq_label_associate(cred
, msqptr
);
717 printf("didn't find it and wasn't asked to create it\n");
724 /* Construct the unique msqid */
725 *retval
= IXSEQ_TO_IPCID(msqid
, msqptr
->u
.msg_perm
);
726 AUDIT_ARG(svipc_id
, *retval
);
729 SYSV_MSG_SUBSYS_UNLOCK();
735 msgsnd(struct proc
*p
, struct msgsnd_args
*uap
, int32_t *retval
)
737 __pthread_testcancel(1);
738 return msgsnd_nocancel(p
, (struct msgsnd_nocancel_args
*)uap
, retval
);
742 msgsnd_nocancel(struct proc
*p
, struct msgsnd_nocancel_args
*uap
, int32_t *retval
)
744 int msqid
= uap
->msqid
;
745 user_addr_t user_msgp
= uap
->msgp
;
746 size_t msgsz
= (size_t)uap
->msgsz
; /* limit to 4G */
747 int msgflg
= uap
->msgflg
;
748 int segs_needed
, eval
;
749 struct msqid_kernel
*msqptr
;
755 SYSV_MSG_SUBSYS_LOCK();
763 printf("call to msgsnd(%d, 0x%qx, %ld, %d)\n", msqid
, user_msgp
, msgsz
,
767 AUDIT_ARG(svipc_id
, msqid
);
768 msqid
= IPCID_TO_IX(msqid
);
770 if (msqid
< 0 || msqid
>= msginfo
.msgmni
) {
772 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid
,
779 msqptr
= &msqids
[msqid
];
780 if (msqptr
->u
.msg_qbytes
== 0) {
782 printf("no such message queue id\n");
787 if (msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
789 printf("wrong sequence number\n");
795 if ((eval
= ipcperm(kauth_cred_get(), &msqptr
->u
.msg_perm
, IPC_W
))) {
797 printf("requester doesn't have write access\n");
803 eval
= mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr
);
808 segs_needed
= (msgsz
+ msginfo
.msgssz
- 1) / msginfo
.msgssz
;
810 printf("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz
, msginfo
.msgssz
,
815 * If we suffer resource starvation, we will sleep in this loop and
816 * wait for more resources to become available. This is a loop to
817 * ensure reacquisition of the mutex following any sleep, since there
818 * are multiple resources under contention.
821 void *blocking_resource
= NULL
;
824 * Check that we have not had the maximum message size change
825 * out from under us and render our message invalid while we
826 * slept waiting for some resource.
828 if (msgsz
> msqptr
->u
.msg_qbytes
) {
830 printf("msgsz > msqptr->msg_qbytes\n");
837 * If the user_msqid_ds is already locked, we need to sleep on
838 * the queue until it's unlocked.
840 if (msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) {
842 printf("msqid is locked\n");
844 blocking_resource
= msqptr
;
848 * If our message plus the messages already in the queue would
849 * cause us to exceed the maximum number of bytes wer are
850 * permitted to queue, then block on the queue until it drains.
852 if (msgsz
+ msqptr
->u
.msg_cbytes
> msqptr
->u
.msg_qbytes
) {
854 printf("msgsz + msg_cbytes > msg_qbytes\n");
856 blocking_resource
= msqptr
;
860 * Both message maps and message headers are protected by
861 * sleeping on the address of the pointer to the list of free
862 * message headers, since they are allocated and freed in
865 if (segs_needed
> nfree_msgmaps
) {
867 printf("segs_needed > nfree_msgmaps\n");
869 blocking_resource
= &free_msghdrs
;
871 if (free_msghdrs
== NULL
) {
873 printf("no more msghdrs\n");
875 blocking_resource
= &free_msghdrs
;
878 if (blocking_resource
!= NULL
) {
881 if ((msgflg
& IPC_NOWAIT
) != 0) {
883 printf("need more resources but caller doesn't want to wait\n");
889 if ((msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0) {
891 printf("we don't own the user_msqid_ds\n");
895 /* Force later arrivals to wait for our
898 printf("we own the user_msqid_ds\n");
900 msqptr
->u
.msg_perm
.mode
|= MSG_LOCKED
;
904 printf("goodnight\n");
906 eval
= msleep(blocking_resource
, &sysv_msg_subsys_mutex
, (PZERO
- 4) | PCATCH
,
909 printf("good morning, eval=%d\n", eval
);
912 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
916 printf("msgsnd: interrupted system call\n");
923 * Make sure that the msq queue still exists
926 if (msqptr
->u
.msg_qbytes
== 0) {
928 printf("msqid deleted\n");
935 printf("got all the resources that we need\n");
942 * We have the resources that we need.
946 if (msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) {
947 panic("msg_perm.mode & MSG_LOCKED");
949 if (segs_needed
> nfree_msgmaps
) {
950 panic("segs_needed > nfree_msgmaps");
952 if (msgsz
+ msqptr
->u
.msg_cbytes
> msqptr
->u
.msg_qbytes
) {
953 panic("msgsz + msg_cbytes > msg_qbytes");
955 if (free_msghdrs
== NULL
) {
956 panic("no more msghdrs");
960 * Re-lock the user_msqid_ds in case we page-fault when copying in
963 if ((msqptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0) {
964 panic("user_msqid_ds is already locked");
966 msqptr
->u
.msg_perm
.mode
|= MSG_LOCKED
;
969 * Allocate a message header
971 msghdr
= free_msghdrs
;
972 free_msghdrs
= msghdr
->msg_next
;
973 msghdr
->msg_spot
= -1;
974 msghdr
->msg_ts
= msgsz
;
977 mac_sysvmsg_label_associate(kauth_cred_get(), msqptr
, msghdr
);
980 * Allocate space for the message
983 while (segs_needed
> 0) {
984 if (nfree_msgmaps
<= 0) {
985 panic("not enough msgmaps");
987 if (free_msgmaps
== -1) {
988 panic("nil free_msgmaps");
992 panic("next too low #1");
994 if (next
>= msginfo
.msgseg
) {
995 panic("next out of range #1");
998 printf("allocating segment %d to message\n", next
);
1000 free_msgmaps
= msgmaps
[next
].next
;
1002 msgmaps
[next
].next
= msghdr
->msg_spot
;
1003 msghdr
->msg_spot
= next
;
1008 * Copy in the message type. For a 64 bit process, this is 64 bits,
1009 * but we only ever use the low 32 bits, so the cast is OK.
1011 if (IS_64BIT_PROCESS(p
)) {
1012 SYSV_MSG_SUBSYS_UNLOCK();
1013 eval
= copyin(user_msgp
, &msgtype
, sizeof(msgtype
));
1014 SYSV_MSG_SUBSYS_LOCK();
1015 msghdr
->msg_type
= CAST_DOWN(long, msgtype
);
1016 user_msgp
= user_msgp
+ sizeof(msgtype
); /* ptr math */
1018 SYSV_MSG_SUBSYS_UNLOCK();
1020 eval
= copyin(user_msgp
, &msg_type32
, sizeof(msg_type32
));
1021 msghdr
->msg_type
= msg_type32
;
1022 SYSV_MSG_SUBSYS_LOCK();
1023 user_msgp
= user_msgp
+ sizeof(msg_type32
); /* ptr math */
1028 printf("error %d copying the message type\n", eval
);
1030 msg_freehdr(msghdr
);
1031 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
1032 wakeup((caddr_t
)msqptr
);
1038 * Validate the message type
1040 if (msghdr
->msg_type
< 1) {
1041 msg_freehdr(msghdr
);
1042 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
1043 wakeup((caddr_t
)msqptr
);
1045 printf("mtype (%ld) < 1\n", msghdr
->msg_type
);
1052 * Copy in the message body
1054 next
= msghdr
->msg_spot
;
1057 /* compare input (size_t) value against restrict (int) value */
1058 if (msgsz
> (size_t)msginfo
.msgssz
) {
1059 tlen
= msginfo
.msgssz
;
1064 panic("next too low #2");
1066 if (next
>= msginfo
.msgseg
) {
1067 panic("next out of range #2");
1070 SYSV_MSG_SUBSYS_UNLOCK();
1071 eval
= copyin(user_msgp
, &msgpool
[next
* msginfo
.msgssz
], tlen
);
1072 SYSV_MSG_SUBSYS_LOCK();
1076 printf("error %d copying in message segment\n", eval
);
1078 msg_freehdr(msghdr
);
1079 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
1080 wakeup((caddr_t
)msqptr
);
1085 user_msgp
= user_msgp
+ tlen
; /* ptr math */
1086 next
= msgmaps
[next
].next
;
1089 panic("didn't use all the msg segments");
1093 * We've got the message. Unlock the user_msqid_ds.
1096 msqptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
1099 * Make sure that the user_msqid_ds is still allocated.
1102 if (msqptr
->u
.msg_qbytes
== 0) {
1103 msg_freehdr(msghdr
);
1104 wakeup((caddr_t
)msqptr
);
1105 /* The SVID says to return EIDRM. */
1109 /* Unfortunately, BSD doesn't define that code yet! */
1117 * Note: Since the task/thread allocates the msghdr and usually
1118 * primes it with its own MAC label, for a majority of policies, it
1119 * won't be necessary to check whether the msghdr has access
1120 * permissions to the msgq. The mac_sysvmsq_check_msqsnd check would
1121 * suffice in that case. However, this hook may be required where
1122 * individual policies derive a non-identical label for the msghdr
1123 * from the current thread label and may want to check the msghdr
1124 * enqueue permissions, along with read/write permissions to the
1127 eval
= mac_sysvmsq_check_enqueue(kauth_cred_get(), msghdr
, msqptr
);
1129 msg_freehdr(msghdr
);
1130 wakeup((caddr_t
) msqptr
);
1135 * Put the message into the queue
1138 if (msqptr
->u
.msg_first
== NULL
) {
1139 msqptr
->u
.msg_first
= msghdr
;
1140 msqptr
->u
.msg_last
= msghdr
;
1142 msqptr
->u
.msg_last
->msg_next
= msghdr
;
1143 msqptr
->u
.msg_last
= msghdr
;
1145 msqptr
->u
.msg_last
->msg_next
= NULL
;
1147 msqptr
->u
.msg_cbytes
+= msghdr
->msg_ts
;
1148 msqptr
->u
.msg_qnum
++;
1149 msqptr
->u
.msg_lspid
= p
->p_pid
;
1150 msqptr
->u
.msg_stime
= sysv_msgtime();
1152 wakeup((caddr_t
)msqptr
);
1157 SYSV_MSG_SUBSYS_UNLOCK();
1163 msgrcv(struct proc
*p
, struct msgrcv_args
*uap
, user_ssize_t
*retval
)
1165 __pthread_testcancel(1);
1166 return msgrcv_nocancel(p
, (struct msgrcv_nocancel_args
*)uap
, retval
);
1170 msgrcv_nocancel(struct proc
*p
, struct msgrcv_nocancel_args
*uap
, user_ssize_t
*retval
)
1172 int msqid
= uap
->msqid
;
1173 user_addr_t user_msgp
= uap
->msgp
;
1174 size_t msgsz
= (size_t)uap
->msgsz
; /* limit to 4G */
1175 long msgtyp
= (long)uap
->msgtyp
; /* limit to 32 bits */
1176 int msgflg
= uap
->msgflg
;
1178 struct msqid_kernel
*msqptr
;
1182 user_long_t msgtype
;
1185 SYSV_MSG_SUBSYS_LOCK();
1193 printf("call to msgrcv(%d, 0x%qx, %ld, %ld, %d)\n", msqid
, user_msgp
,
1194 msgsz
, msgtyp
, msgflg
);
1197 AUDIT_ARG(svipc_id
, msqid
);
1198 msqid
= IPCID_TO_IX(msqid
);
1200 if (msqid
< 0 || msqid
>= msginfo
.msgmni
) {
1202 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid
,
1209 msqptr
= &msqids
[msqid
];
1210 if (msqptr
->u
.msg_qbytes
== 0) {
1212 printf("no such message queue id\n");
1217 if (msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
1219 printf("wrong sequence number\n");
1225 if ((eval
= ipcperm(kauth_cred_get(), &msqptr
->u
.msg_perm
, IPC_R
))) {
1227 printf("requester doesn't have read access\n");
1233 eval
= mac_sysvmsq_check_msqrcv(kauth_cred_get(), msqptr
);
1239 while (msghdr
== NULL
) {
1241 msghdr
= msqptr
->u
.msg_first
;
1242 if (msghdr
!= NULL
) {
1243 if (msgsz
< msghdr
->msg_ts
&&
1244 (msgflg
& MSG_NOERROR
) == 0) {
1246 printf("first message on the queue is too big (want %ld, got %d)\n",
1247 msgsz
, msghdr
->msg_ts
);
1253 eval
= mac_sysvmsq_check_msgrcv(kauth_cred_get(),
1259 if (msqptr
->u
.msg_first
== msqptr
->u
.msg_last
) {
1260 msqptr
->u
.msg_first
= NULL
;
1261 msqptr
->u
.msg_last
= NULL
;
1263 msqptr
->u
.msg_first
= msghdr
->msg_next
;
1264 if (msqptr
->u
.msg_first
== NULL
) {
1265 panic("msg_first/last messed up #1");
1270 struct msg
*previous
;
1274 prev
= &(msqptr
->u
.msg_first
);
1275 while ((msghdr
= *prev
) != NULL
) {
1277 * Is this message's type an exact match or is
1278 * this message's type less than or equal to
1279 * the absolute value of a negative msgtyp?
1280 * Note that the second half of this test can
1281 * NEVER be true if msgtyp is positive since
1282 * msg_type is always positive!
1285 if (msgtyp
== msghdr
->msg_type
||
1286 msghdr
->msg_type
<= -msgtyp
) {
1288 printf("found message type %ld, requested %ld\n",
1289 msghdr
->msg_type
, msgtyp
);
1291 if (msgsz
< msghdr
->msg_ts
&&
1292 (msgflg
& MSG_NOERROR
) == 0) {
1294 printf("requested message on the queue is too big (want %ld, got %d)\n",
1295 msgsz
, msghdr
->msg_ts
);
1301 eval
= mac_sysvmsq_check_msgrcv(
1302 kauth_cred_get(), msghdr
);
1307 *prev
= msghdr
->msg_next
;
1308 if (msghdr
== msqptr
->u
.msg_last
) {
1309 if (previous
== NULL
) {
1311 &msqptr
->u
.msg_first
) {
1312 panic("msg_first/last messed up #2");
1314 msqptr
->u
.msg_first
=
1316 msqptr
->u
.msg_last
=
1320 &msqptr
->u
.msg_first
) {
1321 panic("msg_first/last messed up #3");
1323 msqptr
->u
.msg_last
=
1330 prev
= &(msghdr
->msg_next
);
1335 * We've either extracted the msghdr for the appropriate
1336 * message or there isn't one.
1337 * If there is one then bail out of this loop.
1340 if (msghdr
!= NULL
) {
1345 * Hmph! No message found. Does the user want to wait?
1348 if ((msgflg
& IPC_NOWAIT
) != 0) {
1350 printf("no appropriate message found (msgtyp=%ld)\n",
1353 /* The SVID says to return ENOMSG. */
1357 /* Unfortunately, BSD doesn't define that code yet! */
1364 * Wait for something to happen
1368 printf("msgrcv: goodnight\n");
1370 eval
= msleep((caddr_t
)msqptr
, &sysv_msg_subsys_mutex
, (PZERO
- 4) | PCATCH
, "msgwait",
1373 printf("msgrcv: good morning (eval=%d)\n", eval
);
1378 printf("msgsnd: interrupted system call\n");
1385 * Make sure that the msq queue still exists
1388 if (msqptr
->u
.msg_qbytes
== 0 ||
1389 msqptr
->u
.msg_perm
._seq
!= IPCID_TO_SEQ(uap
->msqid
)) {
1391 printf("msqid deleted\n");
1393 /* The SVID says to return EIDRM. */
1397 /* Unfortunately, BSD doesn't define that code yet! */
1405 * Return the message to the user.
1407 * First, do the bookkeeping (before we risk being interrupted).
1410 msqptr
->u
.msg_cbytes
-= msghdr
->msg_ts
;
1411 msqptr
->u
.msg_qnum
--;
1412 msqptr
->u
.msg_lrpid
= p
->p_pid
;
1413 msqptr
->u
.msg_rtime
= sysv_msgtime();
1416 * Make msgsz the actual amount that we'll be returning.
1417 * Note that this effectively truncates the message if it is too long
1418 * (since msgsz is never increased).
1422 printf("found a message, msgsz=%ld, msg_ts=%d\n", msgsz
,
1425 if (msgsz
> msghdr
->msg_ts
) {
1426 msgsz
= msghdr
->msg_ts
;
1430 * Return the type to the user.
1434 * Copy out the message type. For a 64 bit process, this is 64 bits,
1435 * but we only ever use the low 32 bits, so the cast is OK.
1437 if (IS_64BIT_PROCESS(p
)) {
1438 msgtype
= msghdr
->msg_type
;
1439 SYSV_MSG_SUBSYS_UNLOCK();
1440 eval
= copyout(&msgtype
, user_msgp
, sizeof(msgtype
));
1441 SYSV_MSG_SUBSYS_LOCK();
1442 user_msgp
= user_msgp
+ sizeof(msgtype
); /* ptr math */
1444 msg_type32
= msghdr
->msg_type
;
1445 SYSV_MSG_SUBSYS_UNLOCK();
1446 eval
= copyout(&msg_type32
, user_msgp
, sizeof(msg_type32
));
1447 SYSV_MSG_SUBSYS_LOCK();
1448 user_msgp
= user_msgp
+ sizeof(msg_type32
); /* ptr math */
1453 printf("error (%d) copying out message type\n", eval
);
1455 msg_freehdr(msghdr
);
1456 wakeup((caddr_t
)msqptr
);
1463 * Return the segments to the user
1466 next
= msghdr
->msg_spot
;
1467 for (len
= 0; len
< msgsz
; len
+= msginfo
.msgssz
) {
1470 /* compare input (size_t) value against restrict (int) value */
1471 if (msgsz
> (size_t)msginfo
.msgssz
) {
1472 tlen
= msginfo
.msgssz
;
1477 panic("next too low #3");
1479 if (next
>= msginfo
.msgseg
) {
1480 panic("next out of range #3");
1482 SYSV_MSG_SUBSYS_UNLOCK();
1483 eval
= copyout(&msgpool
[next
* msginfo
.msgssz
],
1485 SYSV_MSG_SUBSYS_LOCK();
1488 printf("error (%d) copying out message segment\n",
1491 msg_freehdr(msghdr
);
1492 wakeup((caddr_t
)msqptr
);
1495 user_msgp
= user_msgp
+ tlen
; /* ptr math */
1496 next
= msgmaps
[next
].next
;
1500 * Done, return the actual number of bytes copied out.
1503 msg_freehdr(msghdr
);
1504 wakeup((caddr_t
)msqptr
);
1508 SYSV_MSG_SUBSYS_UNLOCK();
1513 IPCS_msg_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
1514 __unused
int arg2
, struct sysctl_req
*req
)
1519 struct user32_IPCS_command u32
;
1520 struct user_IPCS_command u64
;
1522 struct user32_msqid_ds msqid_ds32
= {}; /* post conversion, 32 bit version */
1523 struct user64_msqid_ds msqid_ds64
= {}; /* post conversion, 64 bit version */
1527 struct proc
*p
= current_proc();
1529 if (IS_64BIT_PROCESS(p
)) {
1530 ipcs_sz
= sizeof(struct user_IPCS_command
);
1531 msqid_ds_sz
= sizeof(struct user64_msqid_ds
);
1533 ipcs_sz
= sizeof(struct user32_IPCS_command
);
1534 msqid_ds_sz
= sizeof(struct user32_msqid_ds
);
1537 /* Copy in the command structure */
1538 if ((error
= SYSCTL_IN(req
, &ipcs
, ipcs_sz
)) != 0) {
1542 if (!IS_64BIT_PROCESS(p
)) { /* convert in place */
1543 ipcs
.u64
.ipcs_data
= CAST_USER_ADDR_T(ipcs
.u32
.ipcs_data
);
1546 /* Let us version this interface... */
1547 if (ipcs
.u64
.ipcs_magic
!= IPCS_MAGIC
) {
1551 SYSV_MSG_SUBSYS_LOCK();
1553 switch (ipcs
.u64
.ipcs_op
) {
1554 case IPCS_MSG_CONF
: /* Obtain global configuration data */
1555 if (ipcs
.u64
.ipcs_datalen
!= sizeof(struct msginfo
)) {
1559 if (ipcs
.u64
.ipcs_cursor
!= 0) { /* fwd. compat. */
1563 SYSV_MSG_SUBSYS_UNLOCK();
1564 error
= copyout(&msginfo
, ipcs
.u64
.ipcs_data
, ipcs
.u64
.ipcs_datalen
);
1565 SYSV_MSG_SUBSYS_LOCK();
1568 case IPCS_MSG_ITER
: /* Iterate over existing segments */
1569 /* Not done up top so we can set limits via sysctl (later) */
1575 cursor
= ipcs
.u64
.ipcs_cursor
;
1576 if (cursor
< 0 || cursor
>= msginfo
.msgmni
) {
1580 if (ipcs
.u64
.ipcs_datalen
!= (int)msqid_ds_sz
) {
1584 for (; cursor
< msginfo
.msgmni
; cursor
++) {
1585 if (msqids
[cursor
].u
.msg_qbytes
!= 0) { /* allocated */
1590 if (cursor
== msginfo
.msgmni
) {
1595 msqid_dsp
= &msqids
[cursor
]; /* default: 64 bit */
1598 * If necessary, convert the 64 bit kernel segment
1599 * descriptor to a 32 bit user one.
1601 if (IS_64BIT_PROCESS(p
)) {
1602 msqid_ds_kerneltouser64(msqid_dsp
, &msqid_ds64
);
1603 msqid_dsp
= &msqid_ds64
;
1605 msqid_ds_kerneltouser32(msqid_dsp
, &msqid_ds32
);
1606 msqid_dsp
= &msqid_ds32
;
1609 SYSV_MSG_SUBSYS_UNLOCK();
1610 error
= copyout(msqid_dsp
, ipcs
.u64
.ipcs_data
, ipcs
.u64
.ipcs_datalen
);
1613 ipcs
.u64
.ipcs_cursor
= cursor
+ 1;
1615 if (!IS_64BIT_PROCESS(p
)) { /* convert in place */
1616 ipcs
.u32
.ipcs_data
= CAST_DOWN_EXPLICIT(user32_addr_t
, ipcs
.u64
.ipcs_data
);
1618 error
= SYSCTL_OUT(req
, &ipcs
, ipcs_sz
);
1620 SYSV_MSG_SUBSYS_LOCK();
1628 SYSV_MSG_SUBSYS_UNLOCK();
1632 SYSCTL_DECL(_kern_sysv_ipcs
);
1633 SYSCTL_PROC(_kern_sysv_ipcs
, OID_AUTO
, msg
, CTLFLAG_RW
| CTLFLAG_ANYBODY
| CTLFLAG_LOCKED
,
1634 0, 0, IPCS_msg_sysctl
,
1635 "S,IPCS_msg_command",
1636 "ipcs msg command interface");
1638 #endif /* SYSV_MSG */