1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Implementation of SVID messages
30 *
31 * Author:  Daniel Boulet
32 *
33 * Copyright 1993 Daniel Boulet and RTMX Inc.
34 *
35 * This system call was implemented by Daniel Boulet under contract from RTMX.
36 *
37 * Redistribution and use in source forms, with and without modification,
38 * are permitted provided that this entire comment appears intact.
39 *
40 * Redistribution in binary form may occur without any restrictions.
41 * Obviously, it would be nice if you gave credit where credit is due
42 * but requiring it would be too onerous.
43 *
44 * This software is provided ``AS IS'' without any warranties of any kind.
45 */
46/*
47 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
48 * support for mandatory and extensible security protections.  This notice
49 * is included in support of clause 2.2 (b) of the Apple Public License,
50 * Version 2.0.
51 */
52
53#include <sys/param.h>
54#include <sys/systm.h>
55#include <sys/kernel.h>
56#include <sys/proc_internal.h>
57#include <sys/kauth.h>
58#include <sys/msg.h>
59#include <sys/malloc.h>
60#include <mach/mach_types.h>
61
62#include <bsm/audit_kernel.h>
63
64#include <sys/filedesc.h>
65#include <sys/file_internal.h>
66#include <sys/sysctl.h>
67#include <sys/sysproto.h>
68#include <sys/ipcs.h>
69
70#if SYSV_MSG
71
72static int msginit(void *);
73
74#define MSG_DEBUG
75#undef MSG_DEBUG_OK
76
77/* Uncomment this line to see MAC debugging output. */
78/* #define	MAC_DEBUG */
79#if CONFIG_MACF_DEBUG
80#define	MPRINTF(a)	printf(a)
81#else
82#define	MPRINTF(a)
83#endif
84static void msg_freehdr(struct msg *msghdr);
85
86typedef int     sy_call_t(struct proc *, void *, int *);
87
88/* XXX casting to (sy_call_t *) is bogus, as usual. */
89static sy_call_t *msgcalls[] = {
90	(sy_call_t *)msgctl, (sy_call_t *)msgget,
91	(sy_call_t *)msgsnd, (sy_call_t *)msgrcv
92};
93
94static int		nfree_msgmaps;	/* # of free map entries */
95static short		free_msgmaps;	/* free map entries list head */
96static struct msg	*free_msghdrs;	/* list of free msg headers */
97char			*msgpool;	/* MSGMAX byte long msg buffer pool */
98struct msgmap		*msgmaps;	/* MSGSEG msgmap structures */
99struct msg		*msghdrs;	/* MSGTQL msg headers */
100struct msqid_kernel	*msqids;	/* MSGMNI msqid_kernel structs (wrapping user_msqid_ds structs) */
101
102static lck_grp_t       *sysv_msg_subsys_lck_grp;
103static lck_grp_attr_t  *sysv_msg_subsys_lck_grp_attr;
104static lck_attr_t      *sysv_msg_subsys_lck_attr;
105static lck_mtx_t        sysv_msg_subsys_mutex;
106
107#define SYSV_MSG_SUBSYS_LOCK() lck_mtx_lock(&sysv_msg_subsys_mutex)
108#define SYSV_MSG_SUBSYS_UNLOCK() lck_mtx_unlock(&sysv_msg_subsys_mutex)
109
110void sysv_msg_lock_init(void);
111
112
113#ifdef __APPLE_API_PRIVATE
114	int	msgmax,		/* max chars in a message */
115		msgmni,		/* max message queue identifiers */
116		msgmnb,		/* max chars in a queue */
117		msgtql,		/* max messages in system */
118		msgssz,		/* size of a message segment (see notes above) */
119		msgseg;		/* number of message segments */
120struct msginfo msginfo = {
121		MSGMAX,		/* = (MSGSSZ*MSGSEG) : max chars in a message */
122		MSGMNI,		/* = 40 : max message queue identifiers */
123		MSGMNB,		/* = 2048 : max chars in a queue */
124		MSGTQL,		/* = 40 : max messages in system */
125		MSGSSZ,		/* = 8 : size of a message segment (2^N long) */
126		MSGSEG		/* = 2048 : number of message segments */
127};
128#endif /* __APPLE_API_PRIVATE */
129
130/* Initialize the mutex governing access to the SysV msg subsystem */
131__private_extern__ void
132sysv_msg_lock_init( void )
133{
134	sysv_msg_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
135
136	sysv_msg_subsys_lck_grp = lck_grp_alloc_init("sysv_msg_subsys_lock", sysv_msg_subsys_lck_grp_attr);
137
138	sysv_msg_subsys_lck_attr = lck_attr_alloc_init();
139	lck_mtx_init(&sysv_msg_subsys_mutex, sysv_msg_subsys_lck_grp, sysv_msg_subsys_lck_attr);
140}
141
142static __inline__ user_time_t
143sysv_msgtime(void)
144{
145	struct timeval	tv;
146	microtime(&tv);
147	return (tv.tv_sec);
148}
149
150/*
151 * NOTE: Source and target may *NOT* overlap! (target is smaller)
152 */
153static void
154msqid_ds_64to32(struct user_msqid_ds *in, struct msqid_ds *out)
155{
156	out->msg_perm	= in->msg_perm;
157	out->msg_qnum	= in->msg_qnum;
158	out->msg_cbytes	= in->msg_cbytes;	/* for ipcs */
159	out->msg_qbytes	= in->msg_qbytes;
160	out->msg_lspid	= in->msg_lspid;
161	out->msg_lrpid	= in->msg_lrpid;
162	out->msg_stime	= in->msg_stime;	/* XXX loss of range */
163	out->msg_rtime	= in->msg_rtime;	/* XXX loss of range */
164	out->msg_ctime	= in->msg_ctime;	/* XXX loss of range */
165}
166
167/*
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
170 * the beginning.
171 */
172static void
173msqid_ds_32to64(struct msqid_ds *in, struct user_msqid_ds *out)
174{
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;
184}
185
186/* This routine assumes the system is locked prior to calling this routine */
187static int
188msginit(__unused void *dummy)
189{
190	static int initted = 0;
191	register int i;
192
193	/* Lazy initialization on first system call; we don't have SYSINIT(). */
194	if (initted)
195		return (initted);
196
197	/*
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 ...
201	 */
202	i = 8;
203	while (i < 1024 && i != msginfo.msgssz)
204		i <<= 1;
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;
208	}
209
210	if (msginfo.msgseg > 32767) {
211		printf("msginfo.msgseg=%d (> 32767); resetting to %d\n", msginfo.msgseg, MSGSEG);
212		msginfo.msgseg = MSGSEG;
213	}
214
215
216	/*
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).
220	 */
221	msgpool = (char *)_MALLOC(msginfo.msgmax, M_SHM, M_WAITOK);
222	if (msgpool == NULL) {
223		printf("msginit: can't allocate msgpool");
224		goto bad;
225	}
226	MALLOC(msgmaps, struct msgmap *,
227			sizeof(struct msgmap) * msginfo.msgseg,
228			M_SHM, M_WAITOK);
229	if (msgmaps == NULL) {
230		printf("msginit: can't allocate msgmaps");
231		goto bad;
232	}
233
234	MALLOC(msghdrs, struct msg *,
235			sizeof(struct msg) * msginfo.msgtql,
236			M_SHM, M_WAITOK);
237	if (msghdrs == NULL) {
238		printf("msginit: can't allocate msghdrs");
239		goto bad;
240	}
241
242	MALLOC(msqids, struct msqid_kernel *,
243			sizeof(struct user_msqid_ds) * msginfo.msgmni,
244			M_SHM, M_WAITOK);
245	if (msqids == NULL) {
246		printf("msginit: can't allocate msqids");
247		goto bad;
248	}
249
250
251	/* init msgmaps */
252	for (i = 0; i < msginfo.msgseg; i++) {
253		if (i > 0)
254			msgmaps[i-1].next = i;
255		msgmaps[i].next = -1;	/* implies entry is available */
256	}
257	free_msgmaps = 0;
258	nfree_msgmaps = msginfo.msgseg;
259
260
261	/* init msghdrs */
262	for (i = 0; i < msginfo.msgtql; i++) {
263		msghdrs[i].msg_type = 0;
264		if (i > 0)
265			msghdrs[i-1].msg_next = &msghdrs[i];
266		msghdrs[i].msg_next = NULL;
267#if CONFIG_MACF
268		mac_sysvmsg_label_init(&msghdrs[i]);
269#endif
270    	}
271	free_msghdrs = &msghdrs[0];
272
273	/* init msqids */
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;
278#if CONFIG_MACF
279		mac_sysvmsq_label_init(&msqids[i]);
280#endif
281	}
282
283	initted = 1;
284bad:
285	if (!initted) {
286		if (msgpool != NULL)
287			_FREE(msgpool, M_SHM);
288		if (msgmaps != NULL)
289			FREE(msgmaps, M_SHM);
290		if (msghdrs != NULL)
291			FREE(msghdrs, M_SHM);
292		if (msqids != NULL)
293			FREE(msqids, M_SHM);
294	}
295	return (initted);
296}
297
298/*
299 * Entry point for all MSG calls
300 */
301	/* XXX actually varargs. */
302int
303msgsys(struct proc *p, struct msgsys_args *uap, register_t *retval)
304{
305	if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
306		return (EINVAL);
307	return ((*msgcalls[uap->which])(p, &uap->a2, retval));
308}
309
310static void
311msg_freehdr(struct msg *msghdr)
312{
313	while (msghdr->msg_ts > 0) {
314		short next;
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;
320		nfree_msgmaps++;
321		msghdr->msg_spot = next;
322		if (msghdr->msg_ts >= msginfo.msgssz)
323			msghdr->msg_ts -= msginfo.msgssz;
324		else
325			msghdr->msg_ts = 0;
326	}
327	if (msghdr->msg_spot != -1)
328		panic("msghdr->msg_spot != -1");
329	msghdr->msg_next = free_msghdrs;
330	free_msghdrs = msghdr;
331#if CONFIG_MACF
332	mac_sysvmsg_label_recycle(msghdr);
333#endif
334	/*
335	 * Notify waiters that there are free message headers and segments
336	 * now available.
337	 */
338	wakeup((caddr_t)&free_msghdrs);
339}
340
341int
342msgctl(struct proc *p, struct msgctl_args *uap, register_t *retval)
343{
344	int msqid = uap->msqid;
345	int cmd = uap->cmd;
346	kauth_cred_t cred = kauth_cred_get();
347	int rval, eval;
348	struct user_msqid_ds msqbuf;
349	struct msqid_kernel *msqptr;
350	struct user_msqid_ds umsds;
351
352	SYSV_MSG_SUBSYS_LOCK();
353
354	if (!msginit(0)) {
355		eval =  ENOMEM;
356		goto msgctlout;
357	}
358
359#ifdef MSG_DEBUG_OK
360	printf("call to msgctl(%d, %d, 0x%qx)\n", msqid, cmd, uap->buf);
361#endif
362
363	AUDIT_ARG(svipc_cmd, cmd);
364	AUDIT_ARG(svipc_id, msqid);
365	msqid = IPCID_TO_IX(msqid);
366
367	if (msqid < 0 || msqid >= msginfo.msgmni) {
368#ifdef MSG_DEBUG_OK
369		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
370		    msginfo.msgmni);
371#endif
372		eval = EINVAL;
373		goto msgctlout;
374	}
375
376	msqptr = &msqids[msqid];
377
378	if (msqptr->u.msg_qbytes == 0) {
379#ifdef MSG_DEBUG_OK
380		printf("no such msqid\n");
381#endif
382		eval = EINVAL;
383		goto msgctlout;
384	}
385	if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
386#ifdef MSG_DEBUG_OK
387		printf("wrong sequence number\n");
388#endif
389		eval = EINVAL;
390		goto msgctlout;
391	}
392#if CONFIG_MACF
393	eval = mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr, cmd);
394	if (eval)
395		goto msgctlout;
396#endif
397
398	eval = 0;
399	rval = 0;
400
401	switch (cmd) {
402
403	case IPC_RMID:
404	{
405		struct msg *msghdr;
406		if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M)))
407			goto msgctlout;
408#if CONFIG_MACF
409		/*
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.
417		 */
418		for (msghdr = msqptr->u.msg_first; msghdr != NULL;
419		    msghdr = msghdr->msg_next) {
420			eval = mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr);
421			if (eval)
422				goto msgctlout;
423		}
424#endif
425		/* Free the message headers */
426		msghdr = msqptr->u.msg_first;
427		while (msghdr != NULL) {
428			struct msg *msghdr_tmp;
429
430			/* Free the segments of each message */
431			msqptr->u.msg_cbytes -= msghdr->msg_ts;
432			msqptr->u.msg_qnum--;
433			msghdr_tmp = msghdr;
434			msghdr = msghdr->msg_next;
435			msg_freehdr(msghdr_tmp);
436		}
437
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");
442
443		msqptr->u.msg_qbytes = 0;	/* Mark it as free */
444#if CONFIG_MACF
445		mac_sysvmsq_label_recycle(msqptr);
446#endif
447
448		wakeup((caddr_t)msqptr);
449	}
450
451		break;
452
453	case IPC_SET:
454		if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M)))
455			goto msgctlout;
456
457		SYSV_MSG_SUBSYS_UNLOCK();
458
459		if (IS_64BIT_PROCESS(p)) {
460			eval = copyin(uap->buf, &msqbuf, sizeof(struct user_msqid_ds));
461		} else {
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);
465		}
466		if (eval)
467			return(eval);
468
469		SYSV_MSG_SUBSYS_LOCK();
470
471		if (msqbuf.msg_qbytes > msqptr->u.msg_qbytes) {
472			eval = suser(cred, &p->p_acflag);
473			if (eval)
474				goto msgctlout;
475		}
476
477
478		/* compare (msglen_t) value against restrict (int) value */
479		if (msqbuf.msg_qbytes > (msglen_t)msginfo.msgmnb) {
480#ifdef MSG_DEBUG_OK
481			printf("can't increase msg_qbytes beyond %d (truncating)\n",
482			    msginfo.msgmnb);
483#endif
484			msqbuf.msg_qbytes = msginfo.msgmnb;	/* silently restrict qbytes to system limit */
485		}
486		if (msqbuf.msg_qbytes == 0) {
487#ifdef MSG_DEBUG_OK
488			printf("can't reduce msg_qbytes to 0\n");
489#endif
490			eval = EINVAL;
491			goto msgctlout;
492		}
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();
499		break;
500
501	case IPC_STAT:
502		if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_R))) {
503#ifdef MSG_DEBUG_OK
504			printf("requester doesn't have read access\n");
505#endif
506			goto msgctlout;
507		}
508
509		bcopy(msqptr, &umsds, sizeof(struct user_msqid_ds));
510
511		SYSV_MSG_SUBSYS_UNLOCK();
512		if (IS_64BIT_PROCESS(p)) {
513			eval = copyout(&umsds, uap->buf, sizeof(struct user_msqid_ds));
514		} else {
515			struct msqid_ds msqid_ds32;
516			msqid_ds_64to32(&umsds, &msqid_ds32);
517			eval = copyout(&msqid_ds32, uap->buf, sizeof(struct msqid_ds));
518		}
519		SYSV_MSG_SUBSYS_LOCK();
520		break;
521
522	default:
523#ifdef MSG_DEBUG_OK
524		printf("invalid command %d\n", cmd);
525#endif
526		eval = EINVAL;
527		goto msgctlout;
528	}
529
530	if (eval == 0)
531		*retval = rval;
532msgctlout:
533	SYSV_MSG_SUBSYS_UNLOCK();
534	return(eval);
535}
536
537int
538msgget(__unused struct proc *p, struct msgget_args *uap, register_t *retval)
539{
540	int msqid, eval;
541	int key = uap->key;
542	int msgflg = uap->msgflg;
543	kauth_cred_t cred = kauth_cred_get();
544	struct msqid_kernel *msqptr = NULL;
545
546	SYSV_MSG_SUBSYS_LOCK();
547
548	if (!msginit(0)) {
549		eval =  ENOMEM;
550		goto msggetout;
551	}
552
553#ifdef MSG_DEBUG_OK
554	printf("msgget(0x%x, 0%o)\n", key, msgflg);
555#endif
556
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)
562				break;
563		}
564		if (msqid < msginfo.msgmni) {
565#ifdef MSG_DEBUG_OK
566			printf("found public key\n");
567#endif
568			if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
569#ifdef MSG_DEBUG_OK
570				printf("not exclusive\n");
571#endif
572				eval = EEXIST;
573				goto msggetout;
574			}
575			if ((eval = ipcperm(cred, &msqptr->u.msg_perm, msgflg & 0700 ))) {
576#ifdef MSG_DEBUG_OK
577				printf("requester doesn't have 0%o access\n",
578				    msgflg & 0700);
579#endif
580				goto msggetout;
581			}
582#if CONFIG_MACF
583			eval = mac_sysvmsq_check_msqget(cred, msqptr);
584			if (eval)
585				goto msggetout;
586#endif
587			goto found;
588		}
589	}
590
591#ifdef MSG_DEBUG_OK
592	printf("need to allocate the user_msqid_ds\n");
593#endif
594	if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
595		for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
596			/*
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.
601			 */
602			msqptr = &msqids[msqid];
603			if (msqptr->u.msg_qbytes == 0 &&
604			    (msqptr->u.msg_perm.mode & MSG_LOCKED) == 0)
605				break;
606		}
607		if (msqid == msginfo.msgmni) {
608#ifdef MSG_DEBUG_OK
609			printf("no more user_msqid_ds's available\n");
610#endif
611			eval = ENOSPC;
612			goto msggetout;
613		}
614#ifdef MSG_DEBUG_OK
615		printf("msqid %d is available\n", msqid);
616#endif
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();
635#if CONFIG_MACF
636		mac_sysvmsq_label_associate(cred, msqptr);
637#endif
638	} else {
639#ifdef MSG_DEBUG_OK
640		printf("didn't find it and wasn't asked to create it\n");
641#endif
642		eval = ENOENT;
643		goto msggetout;
644	}
645
646found:
647	/* Construct the unique msqid */
648	*retval = IXSEQ_TO_IPCID(msqid, msqptr->u.msg_perm);
649	AUDIT_ARG(svipc_id, *retval);
650	eval = 0;
651msggetout:
652	SYSV_MSG_SUBSYS_UNLOCK();
653	return(eval);
654}
655
656
657int
658msgsnd(struct proc *p, struct msgsnd_args *uap, register_t *retval)
659{
660	__pthread_testcancel(1);
661	return(msgsnd_nocancel(p, (struct msgsnd_nocancel_args *)uap, retval));
662}
663
664int
665msgsnd_nocancel(struct proc *p, struct msgsnd_nocancel_args *uap, register_t *retval)
666{
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;
673	struct msg *msghdr;
674	short next;
675	user_long_t msgtype;
676
677
678	SYSV_MSG_SUBSYS_LOCK();
679
680	if (!msginit(0)) {
681		eval =  ENOMEM;
682		goto msgsndout;
683	}
684
685#ifdef MSG_DEBUG_OK
686	printf("call to msgsnd(%d, 0x%qx, %d, %d)\n", msqid, user_msgp, msgsz,
687	    msgflg);
688#endif
689
690	AUDIT_ARG(svipc_id, msqid);
691	msqid = IPCID_TO_IX(msqid);
692
693	if (msqid < 0 || msqid >= msginfo.msgmni) {
694#ifdef MSG_DEBUG_OK
695		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
696		    msginfo.msgmni);
697#endif
698		eval = EINVAL;
699		goto msgsndout;
700	}
701
702	msqptr = &msqids[msqid];
703	if (msqptr->u.msg_qbytes == 0) {
704#ifdef MSG_DEBUG_OK
705		printf("no such message queue id\n");
706#endif
707		eval = EINVAL;
708		goto msgsndout;
709	}
710	if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
711#ifdef MSG_DEBUG_OK
712		printf("wrong sequence number\n");
713#endif
714		eval = EINVAL;
715		goto msgsndout;
716	}
717
718	if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_W))) {
719#ifdef MSG_DEBUG_OK
720		printf("requester doesn't have write access\n");
721#endif
722		goto msgsndout;
723	}
724
725#if CONFIG_MACF
726	eval = mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr);
727	if (eval)
728		goto msgsndout;
729#endif
730	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
731#ifdef MSG_DEBUG_OK
732	printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
733	    segs_needed);
734#endif
735
736	/*
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.
741	 */
742	for (;;) {
743		void *blocking_resource = NULL;
744
745		/*
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.
749		 */
750		if (msgsz > msqptr->u.msg_qbytes) {
751#ifdef MSG_DEBUG_OK
752			printf("msgsz > msqptr->msg_qbytes\n");
753#endif
754			eval = EINVAL;
755			goto msgsndout;
756		}
757
758		/*
759		 * If the user_msqid_ds is already locked, we need to sleep on
760		 * the queue until it's unlocked.
761		 */
762		if (msqptr->u.msg_perm.mode & MSG_LOCKED) {
763#ifdef MSG_DEBUG_OK
764			printf("msqid is locked\n");
765#endif
766			blocking_resource = msqptr;
767		}
768
769		/*
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.
773		 */
774		if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) {
775#ifdef MSG_DEBUG_OK
776			printf("msgsz + msg_cbytes > msg_qbytes\n");
777#endif
778			blocking_resource = msqptr;
779		}
780
781		/*
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
785		 * tandem.
786		 */
787		if (segs_needed > nfree_msgmaps) {
788#ifdef MSG_DEBUG_OK
789			printf("segs_needed > nfree_msgmaps\n");
790#endif
791			blocking_resource = &free_msghdrs;
792		}
793		if (free_msghdrs == NULL) {
794#ifdef MSG_DEBUG_OK
795			printf("no more msghdrs\n");
796#endif
797			blocking_resource = &free_msghdrs;
798		}
799
800		if (blocking_resource != NULL) {
801			int we_own_it;
802
803			if ((msgflg & IPC_NOWAIT) != 0) {
804#ifdef MSG_DEBUG_OK
805				printf("need more resources but caller doesn't want to wait\n");
806#endif
807				eval = EAGAIN;
808				goto msgsndout;
809			}
810
811			if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
812#ifdef MSG_DEBUG_OK
813				printf("we don't own the user_msqid_ds\n");
814#endif
815				we_own_it = 0;
816			} else {
817				/* Force later arrivals to wait for our
818				   request */
819#ifdef MSG_DEBUG_OK
820				printf("we own the user_msqid_ds\n");
821#endif
822				msqptr->u.msg_perm.mode |= MSG_LOCKED;
823				we_own_it = 1;
824			}
825#ifdef MSG_DEBUG_OK
826			printf("goodnight\n");
827#endif
828			eval = msleep(blocking_resource, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH,
829			    "msgwait", 0);
830#ifdef MSG_DEBUG_OK
831			printf("good morning, eval=%d\n", eval);
832#endif
833			if (we_own_it)
834				msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
835			if (eval != 0) {
836#ifdef MSG_DEBUG_OK
837				printf("msgsnd:  interrupted system call\n");
838#endif
839				eval = EINTR;
840				goto msgsndout;
841			}
842
843			/*
844			 * Make sure that the msq queue still exists
845			 */
846
847			if (msqptr->u.msg_qbytes == 0) {
848#ifdef MSG_DEBUG_OK
849				printf("msqid deleted\n");
850#endif
851				eval = EIDRM;
852				goto msgsndout;
853
854			}
855
856		} else {
857#ifdef MSG_DEBUG_OK
858			printf("got all the resources that we need\n");
859#endif
860			break;
861		}
862	}
863
864	/*
865	 * We have the resources that we need.
866	 * Make sure!
867	 */
868
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");
877
878	/*
879	 * Re-lock the user_msqid_ds in case we page-fault when copying in
880	 * the message
881	 */
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;
885
886	/*
887	 * Allocate a message header
888	 */
889	msghdr = free_msghdrs;
890	free_msghdrs = msghdr->msg_next;
891	msghdr->msg_spot = -1;
892	msghdr->msg_ts = msgsz;
893
894#if CONFIG_MACF
895	mac_sysvmsg_label_associate(kauth_cred_get(), msqptr, msghdr);
896#endif
897	/*
898	 * Allocate space for the message
899	 */
900
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");
906		next = free_msgmaps;
907		if (next <= -1)
908			panic("next too low #1");
909		if (next >= msginfo.msgseg)
910			panic("next out of range #1");
911#ifdef MSG_DEBUG_OK
912		printf("allocating segment %d to message\n", next);
913#endif
914		free_msgmaps = msgmaps[next].next;
915		nfree_msgmaps--;
916		msgmaps[next].next = msghdr->msg_spot;
917		msghdr->msg_spot = next;
918		segs_needed--;
919	}
920
921	/*
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.
924	 */
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 */
931	} else {
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 */
936	}
937
938	if (eval != 0) {
939#ifdef MSG_DEBUG_OK
940		printf("error %d copying the message type\n", eval);
941#endif
942		msg_freehdr(msghdr);
943		msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
944		wakeup((caddr_t)msqptr);
945		goto msgsndout;
946	}
947
948
949	/*
950	 * Validate the message type
951	 */
952	if (msghdr->msg_type < 1) {
953		msg_freehdr(msghdr);
954		msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
955		wakeup((caddr_t)msqptr);
956#ifdef MSG_DEBUG_OK
957		printf("mtype (%d) < 1\n", msghdr->msg_type);
958#endif
959		eval = EINVAL;
960		goto msgsndout;
961	}
962
963	/*
964	 * Copy in the message body
965	 */
966	next = msghdr->msg_spot;
967	while (msgsz > 0) {
968		size_t tlen;
969		/* compare input (size_t) value against restrict (int) value */
970		if (msgsz > (size_t)msginfo.msgssz)
971			tlen = msginfo.msgssz;
972		else
973			tlen = msgsz;
974		if (next <= -1)
975			panic("next too low #2");
976		if (next >= msginfo.msgseg)
977			panic("next out of range #2");
978
979		SYSV_MSG_SUBSYS_UNLOCK();
980		eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen);
981		SYSV_MSG_SUBSYS_LOCK();
982
983		if (eval != 0) {
984#ifdef MSG_DEBUG_OK
985			printf("error %d copying in message segment\n", eval);
986#endif
987			msg_freehdr(msghdr);
988			msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
989			wakeup((caddr_t)msqptr);
990
991			goto msgsndout;
992		}
993		msgsz -= tlen;
994		user_msgp = user_msgp + tlen;	/* ptr math */
995		next = msgmaps[next].next;
996	}
997	if (next != -1)
998		panic("didn't use all the msg segments");
999
1000	/*
1001	 * We've got the message.  Unlock the user_msqid_ds.
1002	 */
1003
1004	msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1005
1006	/*
1007	 * Make sure that the user_msqid_ds is still allocated.
1008	 */
1009
1010	if (msqptr->u.msg_qbytes == 0) {
1011		msg_freehdr(msghdr);
1012		wakeup((caddr_t)msqptr);
1013		/* The SVID says to return EIDRM. */
1014#ifdef EIDRM
1015		eval = EIDRM;
1016#else
1017		/* Unfortunately, BSD doesn't define that code yet! */
1018		eval = EINVAL;
1019#endif
1020		goto msgsndout;
1021	}
1022
1023#if CONFIG_MACF
1024	/*
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
1033	 * msgq.
1034	 */
1035	eval = mac_sysvmsq_check_enqueue(kauth_cred_get(), msghdr, msqptr);
1036	if (eval) {
1037		msg_freehdr(msghdr);
1038		wakeup((caddr_t) msqptr);
1039		goto msgsndout;
1040	}
1041#endif
1042	/*
1043	 * Put the message into the queue
1044	 */
1045
1046	if (msqptr->u.msg_first == NULL) {
1047		msqptr->u.msg_first = msghdr;
1048		msqptr->u.msg_last = msghdr;
1049	} else {
1050		msqptr->u.msg_last->msg_next = msghdr;
1051		msqptr->u.msg_last = msghdr;
1052	}
1053	msqptr->u.msg_last->msg_next = NULL;
1054
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();
1059
1060	wakeup((caddr_t)msqptr);
1061	*retval = 0;
1062	eval = 0;
1063
1064msgsndout:
1065	SYSV_MSG_SUBSYS_UNLOCK();
1066	return(eval);
1067}
1068
1069
1070int
1071msgrcv(struct proc *p, struct msgrcv_args *uap, user_ssize_t *retval)
1072{
1073	__pthread_testcancel(1);
1074	return(msgrcv_nocancel(p, (struct msgrcv_nocancel_args *)uap, retval));
1075}
1076
1077int
1078msgrcv_nocancel(struct proc *p, struct msgrcv_nocancel_args *uap, user_ssize_t *retval)
1079{
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;
1085	size_t len;
1086	struct msqid_kernel *msqptr;
1087	struct msg *msghdr;
1088	int eval;
1089	short next;
1090	user_long_t msgtype;
1091	long msg_type_long;
1092
1093	SYSV_MSG_SUBSYS_LOCK();
1094
1095	if (!msginit(0)) {
1096		eval =  ENOMEM;
1097		goto msgrcvout;
1098	}
1099
1100#ifdef MSG_DEBUG_OK
1101	printf("call to msgrcv(%d, 0x%qx, %d, %ld, %d)\n", msqid, user_msgp,
1102	    msgsz, msgtyp, msgflg);
1103#endif
1104
1105	AUDIT_ARG(svipc_id, msqid);
1106	msqid = IPCID_TO_IX(msqid);
1107
1108	if (msqid < 0 || msqid >= msginfo.msgmni) {
1109#ifdef MSG_DEBUG_OK
1110		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
1111		    msginfo.msgmni);
1112#endif
1113		eval = EINVAL;
1114		goto msgrcvout;
1115	}
1116
1117	msqptr = &msqids[msqid];
1118	if (msqptr->u.msg_qbytes == 0) {
1119#ifdef MSG_DEBUG_OK
1120		printf("no such message queue id\n");
1121#endif
1122		eval = EINVAL;
1123		goto msgrcvout;
1124	}
1125	if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1126#ifdef MSG_DEBUG_OK
1127		printf("wrong sequence number\n");
1128#endif
1129		eval = EINVAL;
1130		goto msgrcvout;
1131	}
1132
1133	if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_R))) {
1134#ifdef MSG_DEBUG_OK
1135		printf("requester doesn't have read access\n");
1136#endif
1137		goto msgrcvout;
1138	}
1139
1140#if CONFIG_MACF
1141	eval = mac_sysvmsq_check_msqrcv(kauth_cred_get(), msqptr);
1142	if (eval)
1143		goto msgrcvout;
1144#endif
1145	msghdr = NULL;
1146	while (msghdr == NULL) {
1147		if (msgtyp == 0) {
1148			msghdr = msqptr->u.msg_first;
1149			if (msghdr != NULL) {
1150				if (msgsz < msghdr->msg_ts &&
1151				    (msgflg & MSG_NOERROR) == 0) {
1152#ifdef MSG_DEBUG_OK
1153					printf("first message on the queue is too big (want %d, got %d)\n",
1154					    msgsz, msghdr->msg_ts);
1155#endif
1156					eval = E2BIG;
1157					goto msgrcvout;
1158				}
1159#if CONFIG_MACF
1160				eval = mac_sysvmsq_check_msgrcv(kauth_cred_get(),
1161				    msghdr);
1162				if (eval)
1163					goto msgrcvout;
1164#endif
1165				if (msqptr->u.msg_first == msqptr->u.msg_last) {
1166					msqptr->u.msg_first = NULL;
1167					msqptr->u.msg_last = NULL;
1168				} else {
1169					msqptr->u.msg_first = msghdr->msg_next;
1170					if (msqptr->u.msg_first == NULL)
1171						panic("msg_first/last messed up #1");
1172				}
1173			}
1174		} else {
1175			struct msg *previous;
1176			struct msg **prev;
1177
1178			previous = NULL;
1179			prev = &(msqptr->u.msg_first);
1180			while ((msghdr = *prev) != NULL) {
1181				/*
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!
1188				 */
1189
1190				if (msgtyp == msghdr->msg_type ||
1191				    msghdr->msg_type <= -msgtyp) {
1192#ifdef MSG_DEBUG_OK
1193					printf("found message type %d, requested %d\n",
1194					    msghdr->msg_type, msgtyp);
1195#endif
1196					if (msgsz < msghdr->msg_ts &&
1197					    (msgflg & MSG_NOERROR) == 0) {
1198#ifdef MSG_DEBUG_OK
1199						printf("requested message on the queue is too big (want %d, got %d)\n",
1200						    msgsz, msghdr->msg_ts);
1201#endif
1202						eval = E2BIG;
1203						goto msgrcvout;
1204					}
1205#if CONFIG_MACF
1206					eval = mac_sysvmsq_check_msgrcv(
1207					    kauth_cred_get(), msghdr);
1208					if (eval)
1209						goto msgrcvout;
1210#endif
1211					*prev = msghdr->msg_next;
1212					if (msghdr == msqptr->u.msg_last) {
1213						if (previous == NULL) {
1214							if (prev !=
1215							    &msqptr->u.msg_first)
1216								panic("msg_first/last messed up #2");
1217							msqptr->u.msg_first =
1218							    NULL;
1219							msqptr->u.msg_last =
1220							    NULL;
1221						} else {
1222							if (prev ==
1223							    &msqptr->u.msg_first)
1224								panic("msg_first/last messed up #3");
1225							msqptr->u.msg_last =
1226							    previous;
1227						}
1228					}
1229					break;
1230				}
1231				previous = msghdr;
1232				prev = &(msghdr->msg_next);
1233			}
1234		}
1235
1236		/*
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.
1240		 */
1241
1242		if (msghdr != NULL)
1243			break;
1244
1245		/*
1246		 * Hmph!  No message found.  Does the user want to wait?
1247		 */
1248
1249		if ((msgflg & IPC_NOWAIT) != 0) {
1250#ifdef MSG_DEBUG_OK
1251			printf("no appropriate message found (msgtyp=%d)\n",
1252			    msgtyp);
1253#endif
1254			/* The SVID says to return ENOMSG. */
1255#ifdef ENOMSG
1256			eval = ENOMSG;
1257#else
1258			/* Unfortunately, BSD doesn't define that code yet! */
1259			eval = EAGAIN;
1260#endif
1261			goto msgrcvout;
1262		}
1263
1264		/*
1265		 * Wait for something to happen
1266		 */
1267
1268#ifdef MSG_DEBUG_OK
1269		printf("msgrcv:  goodnight\n");
1270#endif
1271		eval = msleep((caddr_t)msqptr, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH, "msgwait",
1272		    0);
1273#ifdef MSG_DEBUG_OK
1274		printf("msgrcv:  good morning (eval=%d)\n", eval);
1275#endif
1276
1277		if (eval != 0) {
1278#ifdef MSG_DEBUG_OK
1279			printf("msgsnd:  interrupted system call\n");
1280#endif
1281			eval = EINTR;
1282			goto msgrcvout;
1283		}
1284
1285		/*
1286		 * Make sure that the msq queue still exists
1287		 */
1288
1289		if (msqptr->u.msg_qbytes == 0 ||
1290		    msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1291#ifdef MSG_DEBUG_OK
1292			printf("msqid deleted\n");
1293#endif
1294			/* The SVID says to return EIDRM. */
1295#ifdef EIDRM
1296			eval = EIDRM;
1297#else
1298			/* Unfortunately, BSD doesn't define that code yet! */
1299			eval = EINVAL;
1300#endif
1301			goto msgrcvout;
1302		}
1303	}
1304
1305	/*
1306	 * Return the message to the user.
1307	 *
1308	 * First, do the bookkeeping (before we risk being interrupted).
1309	 */
1310
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();
1315
1316	/*
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).
1320	 */
1321
1322#ifdef MSG_DEBUG_OK
1323	printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
1324	    msghdr->msg_ts);
1325#endif
1326	if (msgsz > msghdr->msg_ts)
1327		msgsz = msghdr->msg_ts;
1328
1329	/*
1330	 * Return the type to the user.
1331	 */
1332
1333	/*
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.
1336	 */
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 */
1343	} else {
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 */
1349	}
1350
1351	if (eval != 0) {
1352#ifdef MSG_DEBUG_OK
1353		printf("error (%d) copying out message type\n", eval);
1354#endif
1355		msg_freehdr(msghdr);
1356		wakeup((caddr_t)msqptr);
1357
1358		goto msgrcvout;
1359	}
1360
1361
1362	/*
1363	 * Return the segments to the user
1364	 */
1365
1366	next = msghdr->msg_spot;
1367	for (len = 0; len < msgsz; len += msginfo.msgssz) {
1368		size_t tlen;
1369
1370		/* compare input (size_t) value against restrict (int) value */
1371		if (msgsz > (size_t)msginfo.msgssz)
1372			tlen = msginfo.msgssz;
1373		else
1374			tlen = msgsz;
1375		if (next <= -1)
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],
1381		    user_msgp, tlen);
1382		SYSV_MSG_SUBSYS_LOCK();
1383		if (eval != 0) {
1384#ifdef MSG_DEBUG_OK
1385			printf("error (%d) copying out message segment\n",
1386			    eval);
1387#endif
1388			msg_freehdr(msghdr);
1389			wakeup((caddr_t)msqptr);
1390			goto msgrcvout;
1391		}
1392		user_msgp = user_msgp + tlen;	/* ptr math */
1393		next = msgmaps[next].next;
1394	}
1395
1396	/*
1397	 * Done, return the actual number of bytes copied out.
1398	 */
1399
1400	msg_freehdr(msghdr);
1401	wakeup((caddr_t)msqptr);
1402	*retval = msgsz;
1403	eval = 0;
1404msgrcvout:
1405	SYSV_MSG_SUBSYS_UNLOCK();
1406	return(eval);
1407}
1408
1409static int
1410IPCS_msg_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
1411	__unused int arg2, struct sysctl_req *req)
1412{
1413	int error;
1414	int cursor;
1415	union {
1416		struct IPCS_command u32;
1417		struct user_IPCS_command u64;
1418	} ipcs;
1419	struct msqid_ds msqid_ds32;	/* post conversion, 32 bit version */
1420	void *msqid_dsp;
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();
1424
1425	if (!IS_64BIT_PROCESS(p)) {
1426		ipcs_sz = sizeof(struct IPCS_command);
1427		msqid_ds_sz = sizeof(struct msqid_ds);
1428	}
1429
1430	/* Copy in the command structure */
1431	if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) {
1432		return(error);
1433	}
1434
1435	if (!IS_64BIT_PROCESS(p))	/* convert in place */
1436		ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data);
1437
1438	/* Let us version this interface... */
1439	if (ipcs.u64.ipcs_magic != IPCS_MAGIC) {
1440		return(EINVAL);
1441	}
1442
1443	SYSV_MSG_SUBSYS_LOCK();
1444
1445	switch(ipcs.u64.ipcs_op) {
1446	case IPCS_MSG_CONF:	/* Obtain global configuration data */
1447		if (ipcs.u64.ipcs_datalen != sizeof(struct msginfo)) {
1448			error = ERANGE;
1449			break;
1450		}
1451		if (ipcs.u64.ipcs_cursor != 0) {	/* fwd. compat. */
1452			error = EINVAL;
1453			break;
1454		}
1455		SYSV_MSG_SUBSYS_UNLOCK();
1456		error = copyout(&msginfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1457		SYSV_MSG_SUBSYS_LOCK();
1458		break;
1459
1460	case IPCS_MSG_ITER:	/* Iterate over existing segments */
1461		/* Not done up top so we can set limits via sysctl (later) */
1462		if (!msginit(0)) {
1463			error =  ENOMEM;
1464			break;
1465		}
1466
1467		cursor = ipcs.u64.ipcs_cursor;
1468		if (cursor < 0 || cursor >= msginfo.msgmni) {
1469			error = ERANGE;
1470			break;
1471		}
1472		if (ipcs.u64.ipcs_datalen != (int)msqid_ds_sz) {
1473			error = EINVAL;
1474			break;
1475		}
1476		for( ; cursor < msginfo.msgmni; cursor++) {
1477			if (msqids[cursor].u.msg_qbytes != 0)	/* allocated */
1478				break;
1479			continue;
1480		}
1481		if (cursor == msginfo.msgmni) {
1482			error = ENOENT;
1483			break;
1484		}
1485
1486		msqid_dsp = &msqids[cursor];	/* default: 64 bit */
1487
1488		/*
1489		 * If necessary, convert the 64 bit kernel segment
1490		 * descriptor to a 32 bit user one.
1491		 */
1492		if (!IS_64BIT_PROCESS(p)) {
1493			msqid_ds_64to32(msqid_dsp, &msqid_ds32);
1494			msqid_dsp = &msqid_ds32;
1495		}
1496		SYSV_MSG_SUBSYS_UNLOCK();
1497		error = copyout(msqid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
1498		if (!error) {
1499			/* update cursor */
1500			ipcs.u64.ipcs_cursor = cursor + 1;
1501
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);
1505		}
1506		SYSV_MSG_SUBSYS_LOCK();
1507		break;
1508
1509	default:
1510		error = EINVAL;
1511		break;
1512	}
1513
1514	SYSV_MSG_SUBSYS_UNLOCK();
1515	return(error);
1516}
1517
1518SYSCTL_DECL(_kern_sysv_ipcs);
1519SYSCTL_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");
1523
1524#endif /* SYSV_MSG */
1525