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