2 * Implementation of SVID messages
4 * Author: Daniel Boulet
6 * Copyright 1993 Daniel Boulet and RTMX Inc.
8 * This system call was implemented by Daniel Boulet under contract from RTMX.
10 * Redistribution and use in source forms, with and without modification,
11 * are permitted provided that this entire comment appears intact.
13 * Redistribution in binary form may occur without any restrictions.
14 * Obviously, it would be nice if you gave credit where credit is due
15 * but requiring it would be too onerous.
17 * This software is provided ``AS IS'' without any warranties of any kind.
20 * Copyright (c) 2003-2005 McAfee, Inc.
21 * All rights reserved.
23 * This software was developed for the FreeBSD Project in part by McAfee
24 * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR
25 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 #include <sys/cdefs.h>
51 __FBSDID("$FreeBSD$");
53 #include "opt_sysvipc.h"
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/sysproto.h>
59 #include <sys/kernel.h>
63 #include <sys/mutex.h>
64 #include <sys/module.h>
66 #include <sys/syscall.h>
67 #include <sys/syscallsubr.h>
68 #include <sys/sysent.h>
69 #include <sys/sysctl.h>
70 #include <sys/malloc.h>
73 #include <security/mac/mac_framework.h>
75 static MALLOC_DEFINE(M_MSG
, "msg", "SVID compatible message queues");
77 static void msginit(void);
78 static int msgunload(void);
79 static int sysvmsg_modload(struct module
*, int, void *);
82 #define DPRINTF(a) printf a
87 static void msg_freehdr(struct msg
*msghdr
);
89 /* XXX casting to (sy_call_t *) is bogus, as usual. */
90 static sy_call_t
*msgcalls
[] = {
91 (sy_call_t
*)msgctl
, (sy_call_t
*)msgget
,
92 (sy_call_t
*)msgsnd
, (sy_call_t
*)msgrcv
96 #define MSGSSZ 8 /* Each segment must be 2^N long */
99 #define MSGSEG 2048 /* must be less than 32767 */
101 #define MSGMAX (MSGSSZ*MSGSEG)
103 #define MSGMNB 2048 /* max # of bytes in a queue */
113 * Based on the configuration parameters described in an SVR2 (yes, two)
114 * config(1m) man page.
116 * Each message is broken up and stored in segments that are msgssz bytes
117 * long. For efficiency reasons, this should be a power of two. Also,
118 * it doesn't make sense if it is less than 8 or greater than about 256.
119 * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
120 * two between 8 and 1024 inclusive (and panic's if it isn't).
122 struct msginfo msginfo
= {
123 MSGMAX
, /* max chars in a message */
124 MSGMNI
, /* # of message queue identifiers */
125 MSGMNB
, /* max chars in a queue */
126 MSGTQL
, /* max messages in system */
127 MSGSSZ
, /* size of a message segment */
128 /* (must be small power of 2 greater than 4) */
129 MSGSEG
/* number of message segments */
133 * macros to convert between msqid_ds's and msqid's.
134 * (specific to this implementation)
136 #define MSQID(ix,ds) ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
137 #define MSQID_IX(id) ((id) & 0xffff)
138 #define MSQID_SEQ(id) (((id) >> 16) & 0xffff)
141 * The rest of this file is specific to this particular implementation.
145 short next
; /* next segment in buffer */
146 /* -1 -> available */
147 /* 0..(MSGSEG-1) -> index of next segment */
150 #define MSG_LOCKED 01000 /* Is this msqid_ds locked? */
152 static int nfree_msgmaps
; /* # of free map entries */
153 static short free_msgmaps
; /* head of linked list of free map entries */
154 static struct msg
*free_msghdrs
;/* list of free msg headers */
155 static char *msgpool
; /* MSGMAX byte long msg buffer pool */
156 static struct msgmap
*msgmaps
; /* MSGSEG msgmap structures */
157 static struct msg
*msghdrs
; /* MSGTQL msg headers */
158 static struct msqid_kernel
*msqids
; /* MSGMNI msqid_kernel struct's */
159 static struct mtx msq_mtx
; /* global mutex for message queues. */
166 TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo
.msgseg
);
167 TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo
.msgssz
);
168 msginfo
.msgmax
= msginfo
.msgseg
* msginfo
.msgssz
;
169 TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo
.msgmni
);
170 TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo
.msgmnb
);
171 TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo
.msgtql
);
173 msgpool
= malloc(msginfo
.msgmax
, M_MSG
, M_WAITOK
);
175 panic("msgpool is NULL");
176 msgmaps
= malloc(sizeof(struct msgmap
) * msginfo
.msgseg
, M_MSG
, M_WAITOK
);
178 panic("msgmaps is NULL");
179 msghdrs
= malloc(sizeof(struct msg
) * msginfo
.msgtql
, M_MSG
, M_WAITOK
);
181 panic("msghdrs is NULL");
182 msqids
= malloc(sizeof(struct msqid_kernel
) * msginfo
.msgmni
, M_MSG
,
185 panic("msqids is NULL");
188 * msginfo.msgssz should be a power of two for efficiency reasons.
189 * It is also pretty silly if msginfo.msgssz is less than 8
190 * or greater than about 256 so ...
194 while (i
< 1024 && i
!= msginfo
.msgssz
)
196 if (i
!= msginfo
.msgssz
) {
197 DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo
.msgssz
,
199 panic("msginfo.msgssz not a small power of 2");
202 if (msginfo
.msgseg
> 32767) {
203 DPRINTF(("msginfo.msgseg=%d\n", msginfo
.msgseg
));
204 panic("msginfo.msgseg > 32767");
208 panic("msgmaps is NULL");
210 for (i
= 0; i
< msginfo
.msgseg
; i
++) {
212 msgmaps
[i
-1].next
= i
;
213 msgmaps
[i
].next
= -1; /* implies entry is available */
216 nfree_msgmaps
= msginfo
.msgseg
;
219 panic("msghdrs is NULL");
221 for (i
= 0; i
< msginfo
.msgtql
; i
++) {
222 msghdrs
[i
].msg_type
= 0;
224 msghdrs
[i
-1].msg_next
= &msghdrs
[i
];
225 msghdrs
[i
].msg_next
= NULL
;
227 mac_sysvmsg_init(&msghdrs
[i
]);
230 free_msghdrs
= &msghdrs
[0];
233 panic("msqids is NULL");
235 for (i
= 0; i
< msginfo
.msgmni
; i
++) {
236 msqids
[i
].u
.msg_qbytes
= 0; /* implies entry is available */
237 msqids
[i
].u
.msg_perm
.seq
= 0; /* reset to a known value */
238 msqids
[i
].u
.msg_perm
.mode
= 0;
240 mac_sysvmsq_init(&msqids
[i
]);
243 mtx_init(&msq_mtx
, "msq", NULL
, MTX_DEF
);
249 struct msqid_kernel
*msqkptr
;
255 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
257 * Look for an unallocated and unlocked msqid_ds.
258 * msqid_ds's can be locked by msgsnd or msgrcv while
259 * they are copying the message in/out. We can't
260 * re-use the entry until they release it.
262 msqkptr
= &msqids
[msqid
];
263 if (msqkptr
->u
.msg_qbytes
!= 0 ||
264 (msqkptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0)
267 if (msqid
!= msginfo
.msgmni
)
271 for (i
= 0; i
< msginfo
.msgtql
; i
++)
272 mac_sysvmsg_destroy(&msghdrs
[i
]);
273 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++)
274 mac_sysvmsq_destroy(&msqids
[msqid
]);
276 free(msgpool
, M_MSG
);
277 free(msgmaps
, M_MSG
);
278 free(msghdrs
, M_MSG
);
280 mtx_destroy(&msq_mtx
);
286 sysvmsg_modload(struct module
*module
, int cmd
, void *arg
)
306 static moduledata_t sysvmsg_mod
= {
312 SYSCALL_MODULE_HELPER(msgsys
);
313 SYSCALL_MODULE_HELPER(msgctl
);
314 SYSCALL_MODULE_HELPER(msgget
);
315 SYSCALL_MODULE_HELPER(msgsnd
);
316 SYSCALL_MODULE_HELPER(msgrcv
);
318 DECLARE_MODULE(sysvmsg
, sysvmsg_mod
,
319 SI_SUB_SYSV_MSG
, SI_ORDER_FIRST
);
320 MODULE_VERSION(sysvmsg
, 1);
323 * Entry point for all MSG calls.
328 /* XXX actually varargs. */
329 struct msgsys_args
/* {
340 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
342 if (uap
->which
< 0 ||
343 uap
->which
>= sizeof(msgcalls
)/sizeof(msgcalls
[0]))
345 error
= (*msgcalls
[uap
->which
])(td
, &uap
->a2
);
353 while (msghdr
->msg_ts
> 0) {
355 if (msghdr
->msg_spot
< 0 || msghdr
->msg_spot
>= msginfo
.msgseg
)
356 panic("msghdr->msg_spot out of range");
357 next
= msgmaps
[msghdr
->msg_spot
].next
;
358 msgmaps
[msghdr
->msg_spot
].next
= free_msgmaps
;
359 free_msgmaps
= msghdr
->msg_spot
;
361 msghdr
->msg_spot
= next
;
362 if (msghdr
->msg_ts
>= msginfo
.msgssz
)
363 msghdr
->msg_ts
-= msginfo
.msgssz
;
367 if (msghdr
->msg_spot
!= -1)
368 panic("msghdr->msg_spot != -1");
369 msghdr
->msg_next
= free_msghdrs
;
370 free_msghdrs
= msghdr
;
372 mac_sysvmsg_cleanup(msghdr
);
376 #ifndef _SYS_SYSPROTO_H_
380 struct msqid_ds
*buf
;
386 register struct msgctl_args
*uap
;
388 int msqid
= uap
->msqid
;
390 struct msqid_ds msqbuf
;
393 DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid
, cmd
, uap
->buf
));
394 if (cmd
== IPC_SET
&&
395 (error
= copyin(uap
->buf
, &msqbuf
, sizeof(msqbuf
))) != 0)
397 error
= kern_msgctl(td
, msqid
, cmd
, &msqbuf
);
398 if (cmd
== IPC_STAT
&& error
== 0)
399 error
= copyout(&msqbuf
, uap
->buf
, sizeof(struct msqid_ds
));
404 kern_msgctl(td
, msqid
, cmd
, msqbuf
)
408 struct msqid_ds
*msqbuf
;
410 int rval
, error
, msqix
;
411 register struct msqid_kernel
*msqkptr
;
413 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
416 msqix
= IPCID_TO_IX(msqid
);
418 if (msqix
< 0 || msqix
>= msginfo
.msgmni
) {
419 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix
,
424 msqkptr
= &msqids
[msqix
];
427 if (msqkptr
->u
.msg_qbytes
== 0) {
428 DPRINTF(("no such msqid\n"));
432 if (msqkptr
->u
.msg_perm
.seq
!= IPCID_TO_SEQ(msqid
)) {
433 DPRINTF(("wrong sequence number\n"));
438 error
= mac_sysvmsq_check_msqctl(td
->td_ucred
, msqkptr
, cmd
);
451 if ((error
= ipcperm(td
, &msqkptr
->u
.msg_perm
, IPC_M
)))
456 * Check that the thread has MAC access permissions to
457 * individual msghdrs. Note: We need to do this in a
458 * separate loop because the actual loop alters the
459 * msq/msghdr info as it progresses, and there is no going
460 * back if half the way through we discover that the
461 * thread cannot free a certain msghdr. The msq will get
462 * into an inconsistent state.
464 for (msghdr
= msqkptr
->u
.msg_first
; msghdr
!= NULL
;
465 msghdr
= msghdr
->msg_next
) {
466 error
= mac_sysvmsq_check_msgrmid(td
->td_ucred
, msghdr
);
472 /* Free the message headers */
473 msghdr
= msqkptr
->u
.msg_first
;
474 while (msghdr
!= NULL
) {
475 struct msg
*msghdr_tmp
;
477 /* Free the segments of each message */
478 msqkptr
->u
.msg_cbytes
-= msghdr
->msg_ts
;
479 msqkptr
->u
.msg_qnum
--;
481 msghdr
= msghdr
->msg_next
;
482 msg_freehdr(msghdr_tmp
);
485 if (msqkptr
->u
.msg_cbytes
!= 0)
486 panic("msg_cbytes is screwed up");
487 if (msqkptr
->u
.msg_qnum
!= 0)
488 panic("msg_qnum is screwed up");
490 msqkptr
->u
.msg_qbytes
= 0; /* Mark it as free */
493 mac_sysvmsq_cleanup(msqkptr
);
502 if ((error
= ipcperm(td
, &msqkptr
->u
.msg_perm
, IPC_M
)))
504 if (msqbuf
->msg_qbytes
> msqkptr
->u
.msg_qbytes
) {
505 error
= priv_check(td
, PRIV_IPC_MSGSIZE
);
509 if (msqbuf
->msg_qbytes
> msginfo
.msgmnb
) {
510 DPRINTF(("can't increase msg_qbytes beyond %d"
511 "(truncating)\n", msginfo
.msgmnb
));
512 msqbuf
->msg_qbytes
= msginfo
.msgmnb
; /* silently restrict qbytes to system limit */
514 if (msqbuf
->msg_qbytes
== 0) {
515 DPRINTF(("can't reduce msg_qbytes to 0\n"));
516 error
= EINVAL
; /* non-standard errno! */
519 msqkptr
->u
.msg_perm
.uid
= msqbuf
->msg_perm
.uid
; /* change the owner */
520 msqkptr
->u
.msg_perm
.gid
= msqbuf
->msg_perm
.gid
; /* change the owner */
521 msqkptr
->u
.msg_perm
.mode
= (msqkptr
->u
.msg_perm
.mode
& ~0777) |
522 (msqbuf
->msg_perm
.mode
& 0777);
523 msqkptr
->u
.msg_qbytes
= msqbuf
->msg_qbytes
;
524 msqkptr
->u
.msg_ctime
= time_second
;
528 if ((error
= ipcperm(td
, &msqkptr
->u
.msg_perm
, IPC_R
))) {
529 DPRINTF(("requester doesn't have read access\n"));
532 *msqbuf
= msqkptr
->u
;
536 DPRINTF(("invalid command %d\n", cmd
));
542 td
->td_retval
[0] = rval
;
544 mtx_unlock(&msq_mtx
);
548 #ifndef _SYS_SYSPROTO_H_
557 register struct msgget_args
*uap
;
559 int msqid
, error
= 0;
561 int msgflg
= uap
->msgflg
;
562 struct ucred
*cred
= td
->td_ucred
;
563 register struct msqid_kernel
*msqkptr
= NULL
;
565 DPRINTF(("msgget(0x%x, 0%o)\n", key
, msgflg
));
567 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
571 if (key
!= IPC_PRIVATE
) {
572 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
573 msqkptr
= &msqids
[msqid
];
574 if (msqkptr
->u
.msg_qbytes
!= 0 &&
575 msqkptr
->u
.msg_perm
.key
== key
)
578 if (msqid
< msginfo
.msgmni
) {
579 DPRINTF(("found public key\n"));
580 if ((msgflg
& IPC_CREAT
) && (msgflg
& IPC_EXCL
)) {
581 DPRINTF(("not exclusive\n"));
585 if ((error
= ipcperm(td
, &msqkptr
->u
.msg_perm
,
587 DPRINTF(("requester doesn't have 0%o access\n",
592 error
= mac_sysvmsq_check_msqget(cred
, msqkptr
);
600 DPRINTF(("need to allocate the msqid_ds\n"));
601 if (key
== IPC_PRIVATE
|| (msgflg
& IPC_CREAT
)) {
602 for (msqid
= 0; msqid
< msginfo
.msgmni
; msqid
++) {
604 * Look for an unallocated and unlocked msqid_ds.
605 * msqid_ds's can be locked by msgsnd or msgrcv while
606 * they are copying the message in/out. We can't
607 * re-use the entry until they release it.
609 msqkptr
= &msqids
[msqid
];
610 if (msqkptr
->u
.msg_qbytes
== 0 &&
611 (msqkptr
->u
.msg_perm
.mode
& MSG_LOCKED
) == 0)
614 if (msqid
== msginfo
.msgmni
) {
615 DPRINTF(("no more msqid_ds's available\n"));
619 DPRINTF(("msqid %d is available\n", msqid
));
620 msqkptr
->u
.msg_perm
.key
= key
;
621 msqkptr
->u
.msg_perm
.cuid
= cred
->cr_uid
;
622 msqkptr
->u
.msg_perm
.uid
= cred
->cr_uid
;
623 msqkptr
->u
.msg_perm
.cgid
= cred
->cr_gid
;
624 msqkptr
->u
.msg_perm
.gid
= cred
->cr_gid
;
625 msqkptr
->u
.msg_perm
.mode
= (msgflg
& 0777);
626 /* Make sure that the returned msqid is unique */
627 msqkptr
->u
.msg_perm
.seq
= (msqkptr
->u
.msg_perm
.seq
+ 1) & 0x7fff;
628 msqkptr
->u
.msg_first
= NULL
;
629 msqkptr
->u
.msg_last
= NULL
;
630 msqkptr
->u
.msg_cbytes
= 0;
631 msqkptr
->u
.msg_qnum
= 0;
632 msqkptr
->u
.msg_qbytes
= msginfo
.msgmnb
;
633 msqkptr
->u
.msg_lspid
= 0;
634 msqkptr
->u
.msg_lrpid
= 0;
635 msqkptr
->u
.msg_stime
= 0;
636 msqkptr
->u
.msg_rtime
= 0;
637 msqkptr
->u
.msg_ctime
= time_second
;
639 mac_sysvmsq_create(cred
, msqkptr
);
642 DPRINTF(("didn't find it and wasn't asked to create it\n"));
648 /* Construct the unique msqid */
649 td
->td_retval
[0] = IXSEQ_TO_IPCID(msqid
, msqkptr
->u
.msg_perm
);
651 mtx_unlock(&msq_mtx
);
655 #ifndef _SYS_SYSPROTO_H_
664 kern_msgsnd(td
, msqid
, msgp
, msgsz
, msgflg
, mtype
)
667 const void *msgp
; /* XXX msgp is actually mtext. */
672 int msqix
, segs_needed
, error
= 0;
673 register struct msqid_kernel
*msqkptr
;
674 register struct msg
*msghdr
;
677 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
681 msqix
= IPCID_TO_IX(msqid
);
683 if (msqix
< 0 || msqix
>= msginfo
.msgmni
) {
684 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix
,
690 msqkptr
= &msqids
[msqix
];
691 if (msqkptr
->u
.msg_qbytes
== 0) {
692 DPRINTF(("no such message queue id\n"));
696 if (msqkptr
->u
.msg_perm
.seq
!= IPCID_TO_SEQ(msqid
)) {
697 DPRINTF(("wrong sequence number\n"));
702 if ((error
= ipcperm(td
, &msqkptr
->u
.msg_perm
, IPC_W
))) {
703 DPRINTF(("requester doesn't have write access\n"));
708 error
= mac_sysvmsq_check_msqsnd(td
->td_ucred
, msqkptr
);
713 segs_needed
= (msgsz
+ msginfo
.msgssz
- 1) / msginfo
.msgssz
;
714 DPRINTF(("msgsz=%zu, msgssz=%d, segs_needed=%d\n", msgsz
,
715 msginfo
.msgssz
, segs_needed
));
717 int need_more_resources
= 0;
721 * (inside this loop in case msg_qbytes changes while we sleep)
724 if (msgsz
> msqkptr
->u
.msg_qbytes
) {
725 DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
730 if (msqkptr
->u
.msg_perm
.mode
& MSG_LOCKED
) {
731 DPRINTF(("msqid is locked\n"));
732 need_more_resources
= 1;
734 if (msgsz
+ msqkptr
->u
.msg_cbytes
> msqkptr
->u
.msg_qbytes
) {
735 DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
736 need_more_resources
= 1;
738 if (segs_needed
> nfree_msgmaps
) {
739 DPRINTF(("segs_needed > nfree_msgmaps\n"));
740 need_more_resources
= 1;
742 if (free_msghdrs
== NULL
) {
743 DPRINTF(("no more msghdrs\n"));
744 need_more_resources
= 1;
747 if (need_more_resources
) {
750 if ((msgflg
& IPC_NOWAIT
) != 0) {
751 DPRINTF(("need more resources but caller "
752 "doesn't want to wait\n"));
757 if ((msqkptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0) {
758 DPRINTF(("we don't own the msqid_ds\n"));
761 /* Force later arrivals to wait for our
763 DPRINTF(("we own the msqid_ds\n"));
764 msqkptr
->u
.msg_perm
.mode
|= MSG_LOCKED
;
767 DPRINTF(("msgsnd: goodnight\n"));
768 error
= msleep(msqkptr
, &msq_mtx
, (PZERO
- 4) | PCATCH
,
770 DPRINTF(("msgsnd: good morning, error=%d\n", error
));
772 msqkptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
773 if (error
== EWOULDBLOCK
) {
774 DPRINTF(("msgsnd: timed out\n"));
778 DPRINTF(("msgsnd: interrupted system call\n"));
784 * Make sure that the msq queue still exists
787 if (msqkptr
->u
.msg_qbytes
== 0) {
788 DPRINTF(("msqid deleted\n"));
794 DPRINTF(("got all the resources that we need\n"));
800 * We have the resources that we need.
804 if (msqkptr
->u
.msg_perm
.mode
& MSG_LOCKED
)
805 panic("msg_perm.mode & MSG_LOCKED");
806 if (segs_needed
> nfree_msgmaps
)
807 panic("segs_needed > nfree_msgmaps");
808 if (msgsz
+ msqkptr
->u
.msg_cbytes
> msqkptr
->u
.msg_qbytes
)
809 panic("msgsz + msg_cbytes > msg_qbytes");
810 if (free_msghdrs
== NULL
)
811 panic("no more msghdrs");
814 * Re-lock the msqid_ds in case we page-fault when copying in the
818 if ((msqkptr
->u
.msg_perm
.mode
& MSG_LOCKED
) != 0)
819 panic("msqid_ds is already locked");
820 msqkptr
->u
.msg_perm
.mode
|= MSG_LOCKED
;
823 * Allocate a message header
826 msghdr
= free_msghdrs
;
827 free_msghdrs
= msghdr
->msg_next
;
828 msghdr
->msg_spot
= -1;
829 msghdr
->msg_ts
= msgsz
;
830 msghdr
->msg_type
= mtype
;
833 * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here
834 * immediately? Or, should it be checked just before the msg is
835 * enqueued in the msgq (as it is done now)?
837 mac_sysvmsg_create(td
->td_ucred
, msqkptr
, msghdr
);
841 * Allocate space for the message
844 while (segs_needed
> 0) {
845 if (nfree_msgmaps
<= 0)
846 panic("not enough msgmaps");
847 if (free_msgmaps
== -1)
848 panic("nil free_msgmaps");
851 panic("next too low #1");
852 if (next
>= msginfo
.msgseg
)
853 panic("next out of range #1");
854 DPRINTF(("allocating segment %d to message\n", next
));
855 free_msgmaps
= msgmaps
[next
].next
;
857 msgmaps
[next
].next
= msghdr
->msg_spot
;
858 msghdr
->msg_spot
= next
;
863 * Validate the message type
866 if (msghdr
->msg_type
< 1) {
868 msqkptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
870 DPRINTF(("mtype (%ld) < 1\n", msghdr
->msg_type
));
876 * Copy in the message body
879 next
= msghdr
->msg_spot
;
882 if (msgsz
> msginfo
.msgssz
)
883 tlen
= msginfo
.msgssz
;
887 panic("next too low #2");
888 if (next
>= msginfo
.msgseg
)
889 panic("next out of range #2");
890 mtx_unlock(&msq_mtx
);
891 if ((error
= copyin(msgp
, &msgpool
[next
* msginfo
.msgssz
],
894 DPRINTF(("error %d copying in message segment\n",
897 msqkptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
903 msgp
= (const char *)msgp
+ tlen
;
904 next
= msgmaps
[next
].next
;
907 panic("didn't use all the msg segments");
910 * We've got the message. Unlock the msqid_ds.
913 msqkptr
->u
.msg_perm
.mode
&= ~MSG_LOCKED
;
916 * Make sure that the msqid_ds is still allocated.
919 if (msqkptr
->u
.msg_qbytes
== 0) {
928 * Note: Since the task/thread allocates the msghdr and usually
929 * primes it with its own MAC label, for a majority of policies, it
930 * won't be necessary to check whether the msghdr has access
931 * permissions to the msgq. The mac_sysvmsq_check_msqsnd check would
932 * suffice in that case. However, this hook may be required where
933 * individual policies derive a non-identical label for the msghdr
934 * from the current thread label and may want to check the msghdr
935 * enqueue permissions, along with read/write permissions to the
938 error
= mac_sysvmsq_check_msgmsq(td
->td_ucred
, msghdr
, msqkptr
);
947 * Put the message into the queue
949 if (msqkptr
->u
.msg_first
== NULL
) {
950 msqkptr
->u
.msg_first
= msghdr
;
951 msqkptr
->u
.msg_last
= msghdr
;
953 msqkptr
->u
.msg_last
->msg_next
= msghdr
;
954 msqkptr
->u
.msg_last
= msghdr
;
956 msqkptr
->u
.msg_last
->msg_next
= NULL
;
958 msqkptr
->u
.msg_cbytes
+= msghdr
->msg_ts
;
959 msqkptr
->u
.msg_qnum
++;
960 msqkptr
->u
.msg_lspid
= td
->td_proc
->p_pid
;
961 msqkptr
->u
.msg_stime
= time_second
;
964 td
->td_retval
[0] = 0;
966 mtx_unlock(&msq_mtx
);
973 register struct msgsnd_args
*uap
;
978 DPRINTF(("call to msgsnd(%d, %p, %zu, %d)\n", uap
->msqid
, uap
->msgp
,
979 uap
->msgsz
, uap
->msgflg
));
981 if ((error
= copyin(uap
->msgp
, &mtype
, sizeof(mtype
))) != 0) {
982 DPRINTF(("error %d copying the message type\n", error
));
985 return (kern_msgsnd(td
, uap
->msqid
,
986 (const char *)uap
->msgp
+ sizeof(mtype
),
987 uap
->msgsz
, uap
->msgflg
, mtype
));
990 #ifndef _SYS_SYSPROTO_H_
1000 kern_msgrcv(td
, msqid
, msgp
, msgsz
, msgtyp
, msgflg
, mtype
)
1003 void *msgp
; /* XXX msgp is actually mtext. */
1010 register struct msqid_kernel
*msqkptr
;
1011 register struct msg
*msghdr
;
1012 int msqix
, error
= 0;
1015 if (!jail_sysvipc_allowed
&& jailed(td
->td_ucred
))
1018 msqix
= IPCID_TO_IX(msqid
);
1020 if (msqix
< 0 || msqix
>= msginfo
.msgmni
) {
1021 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix
,
1026 msqkptr
= &msqids
[msqix
];
1028 if (msqkptr
->u
.msg_qbytes
== 0) {
1029 DPRINTF(("no such message queue id\n"));
1033 if (msqkptr
->u
.msg_perm
.seq
!= IPCID_TO_SEQ(msqid
)) {
1034 DPRINTF(("wrong sequence number\n"));
1039 if ((error
= ipcperm(td
, &msqkptr
->u
.msg_perm
, IPC_R
))) {
1040 DPRINTF(("requester doesn't have read access\n"));
1045 error
= mac_sysvmsq_check_msqrcv(td
->td_ucred
, msqkptr
);
1051 while (msghdr
== NULL
) {
1053 msghdr
= msqkptr
->u
.msg_first
;
1054 if (msghdr
!= NULL
) {
1055 if (msgsz
< msghdr
->msg_ts
&&
1056 (msgflg
& MSG_NOERROR
) == 0) {
1057 DPRINTF(("first message on the queue "
1058 "is too big (want %zu, got %d)\n",
1059 msgsz
, msghdr
->msg_ts
));
1064 error
= mac_sysvmsq_check_msgrcv(td
->td_ucred
,
1069 if (msqkptr
->u
.msg_first
== msqkptr
->u
.msg_last
) {
1070 msqkptr
->u
.msg_first
= NULL
;
1071 msqkptr
->u
.msg_last
= NULL
;
1073 msqkptr
->u
.msg_first
= msghdr
->msg_next
;
1074 if (msqkptr
->u
.msg_first
== NULL
)
1075 panic("msg_first/last screwed up #1");
1079 struct msg
*previous
;
1083 prev
= &(msqkptr
->u
.msg_first
);
1084 while ((msghdr
= *prev
) != NULL
) {
1086 * Is this message's type an exact match or is
1087 * this message's type less than or equal to
1088 * the absolute value of a negative msgtyp?
1089 * Note that the second half of this test can
1090 * NEVER be true if msgtyp is positive since
1091 * msg_type is always positive!
1094 if (msgtyp
== msghdr
->msg_type
||
1095 msghdr
->msg_type
<= -msgtyp
) {
1096 DPRINTF(("found message type %ld, "
1098 msghdr
->msg_type
, msgtyp
));
1099 if (msgsz
< msghdr
->msg_ts
&&
1100 (msgflg
& MSG_NOERROR
) == 0) {
1101 DPRINTF(("requested message "
1102 "on the queue is too big "
1103 "(want %zu, got %hu)\n",
1104 msgsz
, msghdr
->msg_ts
));
1109 error
= mac_sysvmsq_check_msgrcv(
1110 td
->td_ucred
, msghdr
);
1114 *prev
= msghdr
->msg_next
;
1115 if (msghdr
== msqkptr
->u
.msg_last
) {
1116 if (previous
== NULL
) {
1118 &msqkptr
->u
.msg_first
)
1119 panic("msg_first/last screwed up #2");
1120 msqkptr
->u
.msg_first
=
1122 msqkptr
->u
.msg_last
=
1126 &msqkptr
->u
.msg_first
)
1127 panic("msg_first/last screwed up #3");
1128 msqkptr
->u
.msg_last
=
1135 prev
= &(msghdr
->msg_next
);
1140 * We've either extracted the msghdr for the appropriate
1141 * message or there isn't one.
1142 * If there is one then bail out of this loop.
1149 * Hmph! No message found. Does the user want to wait?
1152 if ((msgflg
& IPC_NOWAIT
) != 0) {
1153 DPRINTF(("no appropriate message found (msgtyp=%ld)\n",
1155 /* The SVID says to return ENOMSG. */
1161 * Wait for something to happen
1164 DPRINTF(("msgrcv: goodnight\n"));
1165 error
= msleep(msqkptr
, &msq_mtx
, (PZERO
- 4) | PCATCH
,
1167 DPRINTF(("msgrcv: good morning (error=%d)\n", error
));
1170 DPRINTF(("msgrcv: interrupted system call\n"));
1176 * Make sure that the msq queue still exists
1179 if (msqkptr
->u
.msg_qbytes
== 0 ||
1180 msqkptr
->u
.msg_perm
.seq
!= IPCID_TO_SEQ(msqid
)) {
1181 DPRINTF(("msqid deleted\n"));
1188 * Return the message to the user.
1190 * First, do the bookkeeping (before we risk being interrupted).
1193 msqkptr
->u
.msg_cbytes
-= msghdr
->msg_ts
;
1194 msqkptr
->u
.msg_qnum
--;
1195 msqkptr
->u
.msg_lrpid
= td
->td_proc
->p_pid
;
1196 msqkptr
->u
.msg_rtime
= time_second
;
1199 * Make msgsz the actual amount that we'll be returning.
1200 * Note that this effectively truncates the message if it is too long
1201 * (since msgsz is never increased).
1204 DPRINTF(("found a message, msgsz=%zu, msg_ts=%hu\n", msgsz
,
1206 if (msgsz
> msghdr
->msg_ts
)
1207 msgsz
= msghdr
->msg_ts
;
1208 *mtype
= msghdr
->msg_type
;
1211 * Return the segments to the user
1214 next
= msghdr
->msg_spot
;
1215 for (len
= 0; len
< msgsz
; len
+= msginfo
.msgssz
) {
1218 if (msgsz
- len
> msginfo
.msgssz
)
1219 tlen
= msginfo
.msgssz
;
1223 panic("next too low #3");
1224 if (next
>= msginfo
.msgseg
)
1225 panic("next out of range #3");
1226 mtx_unlock(&msq_mtx
);
1227 error
= copyout(&msgpool
[next
* msginfo
.msgssz
], msgp
, tlen
);
1230 DPRINTF(("error (%d) copying out message segment\n",
1232 msg_freehdr(msghdr
);
1236 msgp
= (char *)msgp
+ tlen
;
1237 next
= msgmaps
[next
].next
;
1241 * Done, return the actual number of bytes copied out.
1244 msg_freehdr(msghdr
);
1246 td
->td_retval
[0] = msgsz
;
1248 mtx_unlock(&msq_mtx
);
1255 register struct msgrcv_args
*uap
;
1260 DPRINTF(("call to msgrcv(%d, %p, %zu, %ld, %d)\n", uap
->msqid
,
1261 uap
->msgp
, uap
->msgsz
, uap
->msgtyp
, uap
->msgflg
));
1263 if ((error
= kern_msgrcv(td
, uap
->msqid
,
1264 (char *)uap
->msgp
+ sizeof(mtype
), uap
->msgsz
,
1265 uap
->msgtyp
, uap
->msgflg
, &mtype
)) != 0)
1267 if ((error
= copyout(&mtype
, uap
->msgp
, sizeof(mtype
))) != 0)
1268 DPRINTF(("error %d copying the message type\n", error
));
1273 sysctl_msqids(SYSCTL_HANDLER_ARGS
)
1276 return (SYSCTL_OUT(req
, msqids
,
1277 sizeof(struct msqid_kernel
) * msginfo
.msgmni
));
1280 SYSCTL_INT(_kern_ipc
, OID_AUTO
, msgmax
, CTLFLAG_RD
, &msginfo
.msgmax
, 0,
1281 "Maximum message size");
1282 SYSCTL_INT(_kern_ipc
, OID_AUTO
, msgmni
, CTLFLAG_RDTUN
, &msginfo
.msgmni
, 0,
1283 "Number of message queue identifiers");
1284 SYSCTL_INT(_kern_ipc
, OID_AUTO
, msgmnb
, CTLFLAG_RDTUN
, &msginfo
.msgmnb
, 0,
1285 "Maximum number of bytes in a queue");
1286 SYSCTL_INT(_kern_ipc
, OID_AUTO
, msgtql
, CTLFLAG_RDTUN
, &msginfo
.msgtql
, 0,
1287 "Maximum number of messages in the system");
1288 SYSCTL_INT(_kern_ipc
, OID_AUTO
, msgssz
, CTLFLAG_RDTUN
, &msginfo
.msgssz
, 0,
1289 "Size of a message segment");
1290 SYSCTL_INT(_kern_ipc
, OID_AUTO
, msgseg
, CTLFLAG_RDTUN
, &msginfo
.msgseg
, 0,
1291 "Number of message segments");
1292 SYSCTL_PROC(_kern_ipc
, OID_AUTO
, msqids
, CTLFLAG_RD
,
1293 NULL
, 0, sysctl_msqids
, "", "Message queue IDs");