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