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