]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/sysv_msg.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / kern / sysv_msg.c
CommitLineData
1c79356b 1/*
cb323159 2 * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
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.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
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 */
2d21ac55
A
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 */
1c79356b
A
52
53#include <sys/param.h>
54#include <sys/systm.h>
1c79356b 55#include <sys/kernel.h>
91447636
A
56#include <sys/proc_internal.h>
57#include <sys/kauth.h>
1c79356b 58#include <sys/msg.h>
91447636
A
59#include <sys/malloc.h>
60#include <mach/mach_types.h>
e5568f75 61
b0d623f7 62#include <security/audit/audit.h>
1c79356b 63
91447636
A
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
5ba3f43e
A
70#if CONFIG_MACF
71#include <security/mac_framework.h>
72#endif
73
2d21ac55
A
74#if SYSV_MSG
75
76static int msginit(void *);
1c79356b
A
77
78#define MSG_DEBUG
79#undef MSG_DEBUG_OK
80
2d21ac55
A
81/* Uncomment this line to see MAC debugging output. */
82/* #define MAC_DEBUG */
83#if CONFIG_MACF_DEBUG
0a7de745 84#define MPRINTF(a) printf(a)
2d21ac55 85#else
0a7de745 86#define MPRINTF(a)
2d21ac55 87#endif
91447636
A
88static void msg_freehdr(struct msg *msghdr);
89
90typedef int sy_call_t(struct proc *, void *, int *);
1c79356b
A
91
92/* XXX casting to (sy_call_t *) is bogus, as usual. */
cb323159 93static sy_call_t* const msgcalls[] = {
1c79356b
A
94 (sy_call_t *)msgctl, (sy_call_t *)msgget,
95 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv
96};
97
0a7de745
A
98static int nfree_msgmaps; /* # of free map entries */
99static short free_msgmaps; /* free map entries list head */
100static struct msg *free_msghdrs; /* list of free msg headers */
101char *msgpool; /* MSGMAX byte long msg buffer pool */
102struct msgmap *msgmaps; /* MSGSEG msgmap structures */
103struct msg *msghdrs; /* MSGTQL msg headers */
104struct msqid_kernel *msqids; /* MSGMNI msqid_kernel structs (wrapping user_msqid_ds structs) */
91447636
A
105
106static lck_grp_t *sysv_msg_subsys_lck_grp;
107static lck_grp_attr_t *sysv_msg_subsys_lck_grp_attr;
108static lck_attr_t *sysv_msg_subsys_lck_attr;
109static 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
114void sysv_msg_lock_init(void);
115
116
117#ifdef __APPLE_API_PRIVATE
0a7de745
A
118int 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 */
91447636 124struct msginfo msginfo = {
cb323159
A
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 */
91447636
A
131};
132#endif /* __APPLE_API_PRIVATE */
133
134/* Initialize the mutex governing access to the SysV msg subsystem */
135__private_extern__ void
136sysv_msg_lock_init( void )
137{
138 sysv_msg_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
91447636
A
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();
91447636
A
143 lck_mtx_init(&sysv_msg_subsys_mutex, sysv_msg_subsys_lck_grp, sysv_msg_subsys_lck_attr);
144}
145
146static __inline__ user_time_t
147sysv_msgtime(void)
148{
0a7de745 149 struct timeval tv;
91447636 150 microtime(&tv);
0a7de745 151 return tv.tv_sec;
91447636
A
152}
153
154/*
155 * NOTE: Source and target may *NOT* overlap! (target is smaller)
156 */
157static void
b0d623f7
A
158msqid_ds_kerneltouser32(struct user_msqid_ds *in, struct user32_msqid_ds *out)
159{
0a7de745
A
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 */
b0d623f7
A
169}
170
171static void
172msqid_ds_kerneltouser64(struct user_msqid_ds *in, struct user64_msqid_ds *out)
91447636 173{
0a7de745
A
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 */
91447636
A
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 */
190static void
b0d623f7
A
191msqid_ds_user32tokernel(struct user32_msqid_ds *in, struct user_msqid_ds *out)
192{
0a7de745
A
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;
b0d623f7
A
202}
203
204static void
205msqid_ds_user64tokernel(struct user64_msqid_ds *in, struct user_msqid_ds *out)
91447636 206{
0a7de745
A
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;
91447636
A
216}
217
218/* This routine assumes the system is locked prior to calling this routine */
2d21ac55 219static int
91447636 220msginit(__unused void *dummy)
1c79356b 221{
91447636 222 static int initted = 0;
39037602 223 int i;
1c79356b 224
91447636 225 /* Lazy initialization on first system call; we don't have SYSINIT(). */
0a7de745
A
226 if (initted) {
227 return initted;
228 }
91447636 229
1c79356b
A
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 */
1c79356b 235 i = 8;
0a7de745 236 while (i < 1024 && i != msginfo.msgssz) {
1c79356b 237 i <<= 1;
0a7de745
A
238 }
239 if (i != msginfo.msgssz) {
2d21ac55
A
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;
1c79356b
A
242 }
243
244 if (msginfo.msgseg > 32767) {
2d21ac55
A
245 printf("msginfo.msgseg=%d (> 32767); resetting to %d\n", msginfo.msgseg, MSGSEG);
246 msginfo.msgseg = MSGSEG;
1c79356b
A
247 }
248
1c79356b 249
2d21ac55
A
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 *,
0a7de745
A
261 sizeof(struct msgmap) * msginfo.msgseg,
262 M_SHM, M_WAITOK);
2d21ac55
A
263 if (msgmaps == NULL) {
264 printf("msginit: can't allocate msgmaps");
265 goto bad;
266 }
267
268 MALLOC(msghdrs, struct msg *,
0a7de745
A
269 sizeof(struct msg) * msginfo.msgtql,
270 M_SHM, M_WAITOK);
2d21ac55
A
271 if (msghdrs == NULL) {
272 printf("msginit: can't allocate msghdrs");
273 goto bad;
274 }
275
276 MALLOC(msqids, struct msqid_kernel *,
0a7de745
A
277 sizeof(struct msqid_kernel) * msginfo.msgmni,
278 M_SHM, M_WAITOK);
2d21ac55
A
279 if (msqids == NULL) {
280 printf("msginit: can't allocate msqids");
281 goto bad;
282 }
283
284
285 /* init msgmaps */
1c79356b 286 for (i = 0; i < msginfo.msgseg; i++) {
0a7de745
A
287 if (i > 0) {
288 msgmaps[i - 1].next = i;
289 }
290 msgmaps[i].next = -1; /* implies entry is available */
1c79356b
A
291 }
292 free_msgmaps = 0;
293 nfree_msgmaps = msginfo.msgseg;
294
1c79356b 295
2d21ac55 296 /* init msghdrs */
1c79356b
A
297 for (i = 0; i < msginfo.msgtql; i++) {
298 msghdrs[i].msg_type = 0;
0a7de745
A
299 if (i > 0) {
300 msghdrs[i - 1].msg_next = &msghdrs[i];
301 }
1c79356b 302 msghdrs[i].msg_next = NULL;
2d21ac55
A
303#if CONFIG_MACF
304 mac_sysvmsg_label_init(&msghdrs[i]);
305#endif
0a7de745 306 }
1c79356b
A
307 free_msghdrs = &msghdrs[0];
308
2d21ac55 309 /* init msqids */
1c79356b 310 for (i = 0; i < msginfo.msgmni; i++) {
0a7de745
A
311 msqids[i].u.msg_qbytes = 0; /* implies entry is available */
312 msqids[i].u.msg_perm._seq = 0; /* reset to a known value */
2d21ac55
A
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;
320bad:
321 if (!initted) {
0a7de745 322 if (msgpool != NULL) {
2d21ac55 323 _FREE(msgpool, M_SHM);
0a7de745
A
324 }
325 if (msgmaps != NULL) {
2d21ac55 326 FREE(msgmaps, M_SHM);
0a7de745
A
327 }
328 if (msghdrs != NULL) {
2d21ac55 329 FREE(msghdrs, M_SHM);
0a7de745
A
330 }
331 if (msqids != NULL) {
2d21ac55 332 FREE(msqids, M_SHM);
0a7de745 333 }
1c79356b 334 }
0a7de745 335 return initted;
1c79356b
A
336}
337
338/*
b0d623f7
A
339 * msgsys
340 *
341 * Entry point for all MSG calls: msgctl, msgget, msgsnd, msgrcv
342 *
343 * Parameters: p Process requesting the call
0a7de745
A
344 * uap User argument descriptor (see below)
345 * retval Return value of the selected msg call
b0d623f7
A
346 *
347 * Indirect parameters: uap->which msg call to invoke (index in array of msg calls)
0a7de745
A
348 * uap->a2 User argument descriptor
349 *
b0d623f7 350 * Returns: 0 Success
0a7de745 351 * !0 Not success
b0d623f7
A
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
0a7de745
A
356 * functions (msgctl, msgget, msgsnd, msgrcv). The correct
357 * usage is to call the other MSG functions directly.
b0d623f7 358 *
1c79356b 359 */
91447636 360int
b0d623f7 361msgsys(struct proc *p, struct msgsys_args *uap, int32_t *retval)
1c79356b 362{
0a7de745
A
363 if (uap->which >= sizeof(msgcalls) / sizeof(msgcalls[0])) {
364 return EINVAL;
365 }
366 return (*msgcalls[uap->which])(p, &uap->a2, retval);
1c79356b
A
367}
368
369static void
91447636 370msg_freehdr(struct msg *msghdr)
1c79356b
A
371{
372 while (msghdr->msg_ts > 0) {
373 short next;
0a7de745 374 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) {
1c79356b 375 panic("msghdr->msg_spot out of range");
0a7de745 376 }
1c79356b
A
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;
0a7de745 382 if (msghdr->msg_ts >= msginfo.msgssz) {
1c79356b 383 msghdr->msg_ts -= msginfo.msgssz;
0a7de745 384 } else {
1c79356b 385 msghdr->msg_ts = 0;
0a7de745 386 }
1c79356b 387 }
0a7de745 388 if (msghdr->msg_spot != -1) {
1c79356b 389 panic("msghdr->msg_spot != -1");
0a7de745 390 }
1c79356b
A
391 msghdr->msg_next = free_msghdrs;
392 free_msghdrs = msghdr;
2d21ac55
A
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);
1c79356b
A
401}
402
1c79356b 403int
b0d623f7 404msgctl(struct proc *p, struct msgctl_args *uap, int32_t *retval)
1c79356b
A
405{
406 int msqid = uap->msqid;
407 int cmd = uap->cmd;
91447636 408 kauth_cred_t cred = kauth_cred_get();
1c79356b 409 int rval, eval;
91447636 410 struct user_msqid_ds msqbuf;
2d21ac55 411 struct msqid_kernel *msqptr;
91447636
A
412
413 SYSV_MSG_SUBSYS_LOCK();
414
2d21ac55
A
415 if (!msginit(0)) {
416 eval = ENOMEM;
417 goto msgctlout;
418 }
1c79356b
A
419
420#ifdef MSG_DEBUG_OK
91447636 421 printf("call to msgctl(%d, %d, 0x%qx)\n", msqid, cmd, uap->buf);
1c79356b
A
422#endif
423
55e303ae
A
424 AUDIT_ARG(svipc_cmd, cmd);
425 AUDIT_ARG(svipc_id, msqid);
1c79356b
A
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
91447636
A
433 eval = EINVAL;
434 goto msgctlout;
1c79356b
A
435 }
436
437 msqptr = &msqids[msqid];
438
2d21ac55 439 if (msqptr->u.msg_qbytes == 0) {
1c79356b
A
440#ifdef MSG_DEBUG_OK
441 printf("no such msqid\n");
442#endif
91447636
A
443 eval = EINVAL;
444 goto msgctlout;
1c79356b 445 }
2d21ac55 446 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1c79356b
A
447#ifdef MSG_DEBUG_OK
448 printf("wrong sequence number\n");
449#endif
91447636
A
450 eval = EINVAL;
451 goto msgctlout;
1c79356b 452 }
2d21ac55
A
453#if CONFIG_MACF
454 eval = mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr, cmd);
0a7de745 455 if (eval) {
2d21ac55 456 goto msgctlout;
0a7de745 457 }
2d21ac55 458#endif
1c79356b
A
459
460 eval = 0;
461 rval = 0;
462
463 switch (cmd) {
1c79356b
A
464 case IPC_RMID:
465 {
466 struct msg *msghdr;
0a7de745 467 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) {
91447636 468 goto msgctlout;
0a7de745 469 }
2d21ac55
A
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);
0a7de745 483 if (eval) {
2d21ac55 484 goto msgctlout;
0a7de745 485 }
2d21ac55
A
486 }
487#endif
1c79356b 488 /* Free the message headers */
2d21ac55 489 msghdr = msqptr->u.msg_first;
1c79356b
A
490 while (msghdr != NULL) {
491 struct msg *msghdr_tmp;
492
493 /* Free the segments of each message */
2d21ac55
A
494 msqptr->u.msg_cbytes -= msghdr->msg_ts;
495 msqptr->u.msg_qnum--;
1c79356b
A
496 msghdr_tmp = msghdr;
497 msghdr = msghdr->msg_next;
498 msg_freehdr(msghdr_tmp);
499 }
500
0a7de745 501 if (msqptr->u.msg_cbytes != 0) {
1c79356b 502 panic("msg_cbytes is messed up");
0a7de745
A
503 }
504 if (msqptr->u.msg_qnum != 0) {
1c79356b 505 panic("msg_qnum is messed up");
0a7de745 506 }
1c79356b 507
0a7de745 508 msqptr->u.msg_qbytes = 0; /* Mark it as free */
2d21ac55
A
509#if CONFIG_MACF
510 mac_sysvmsq_label_recycle(msqptr);
511#endif
1c79356b
A
512
513 wakeup((caddr_t)msqptr);
514 }
515
0a7de745 516 break;
1c79356b
A
517
518 case IPC_SET:
0a7de745 519 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) {
91447636 520 goto msgctlout;
0a7de745 521 }
91447636
A
522
523 SYSV_MSG_SUBSYS_UNLOCK();
524
525 if (IS_64BIT_PROCESS(p)) {
b0d623f7
A
526 struct user64_msqid_ds tmpds;
527 eval = copyin(uap->buf, &tmpds, sizeof(tmpds));
528
529 msqid_ds_user64tokernel(&tmpds, &msqbuf);
91447636 530 } else {
b0d623f7
A
531 struct user32_msqid_ds tmpds;
532
533 eval = copyin(uap->buf, &tmpds, sizeof(tmpds));
534
535 msqid_ds_user32tokernel(&tmpds, &msqbuf);
91447636 536 }
0a7de745
A
537 if (eval) {
538 return eval;
539 }
91447636
A
540
541 SYSV_MSG_SUBSYS_LOCK();
542
2d21ac55 543 if (msqbuf.msg_qbytes > msqptr->u.msg_qbytes) {
1c79356b 544 eval = suser(cred, &p->p_acflag);
0a7de745 545 if (eval) {
91447636 546 goto msgctlout;
0a7de745 547 }
1c79356b 548 }
91447636
A
549
550
551 /* compare (msglen_t) value against restrict (int) value */
b0d623f7 552 if (msqbuf.msg_qbytes > (user_msglen_t)msginfo.msgmnb) {
1c79356b
A
553#ifdef MSG_DEBUG_OK
554 printf("can't increase msg_qbytes beyond %d (truncating)\n",
555 msginfo.msgmnb);
556#endif
0a7de745 557 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */
1c79356b
A
558 }
559 if (msqbuf.msg_qbytes == 0) {
560#ifdef MSG_DEBUG_OK
561 printf("can't reduce msg_qbytes to 0\n");
562#endif
91447636
A
563 eval = EINVAL;
564 goto msgctlout;
1c79356b 565 }
0a7de745
A
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 */
2d21ac55 568 msqptr->u.msg_perm.mode = (msqptr->u.msg_perm.mode & ~0777) |
1c79356b 569 (msqbuf.msg_perm.mode & 0777);
2d21ac55
A
570 msqptr->u.msg_qbytes = msqbuf.msg_qbytes;
571 msqptr->u.msg_ctime = sysv_msgtime();
1c79356b
A
572 break;
573
574 case IPC_STAT:
2d21ac55 575 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_R))) {
1c79356b
A
576#ifdef MSG_DEBUG_OK
577 printf("requester doesn't have read access\n");
578#endif
91447636
A
579 goto msgctlout;
580 }
581
91447636
A
582 SYSV_MSG_SUBSYS_UNLOCK();
583 if (IS_64BIT_PROCESS(p)) {
490019cf 584 struct user64_msqid_ds msqid_ds64 = {};
b0d623f7
A
585 msqid_ds_kerneltouser64(&msqptr->u, &msqid_ds64);
586 eval = copyout(&msqid_ds64, uap->buf, sizeof(msqid_ds64));
91447636 587 } else {
490019cf 588 struct user32_msqid_ds msqid_ds32 = {};
b0d623f7
A
589 msqid_ds_kerneltouser32(&msqptr->u, &msqid_ds32);
590 eval = copyout(&msqid_ds32, uap->buf, sizeof(msqid_ds32));
1c79356b 591 }
91447636 592 SYSV_MSG_SUBSYS_LOCK();
1c79356b
A
593 break;
594
595 default:
596#ifdef MSG_DEBUG_OK
597 printf("invalid command %d\n", cmd);
598#endif
91447636
A
599 eval = EINVAL;
600 goto msgctlout;
1c79356b
A
601 }
602
0a7de745 603 if (eval == 0) {
91447636 604 *retval = rval;
0a7de745 605 }
91447636
A
606msgctlout:
607 SYSV_MSG_SUBSYS_UNLOCK();
0a7de745 608 return eval;
1c79356b
A
609}
610
1c79356b 611int
b0d623f7 612msgget(__unused struct proc *p, struct msgget_args *uap, int32_t *retval)
1c79356b
A
613{
614 int msqid, eval;
615 int key = uap->key;
616 int msgflg = uap->msgflg;
91447636 617 kauth_cred_t cred = kauth_cred_get();
2d21ac55 618 struct msqid_kernel *msqptr = NULL;
91447636
A
619
620 SYSV_MSG_SUBSYS_LOCK();
2d21ac55
A
621
622 if (!msginit(0)) {
623 eval = ENOMEM;
624 goto msggetout;
625 }
1c79356b
A
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];
2d21ac55 634 if (msqptr->u.msg_qbytes != 0 &&
0a7de745 635 msqptr->u.msg_perm._key == key) {
1c79356b 636 break;
0a7de745 637 }
1c79356b
A
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
91447636
A
647 eval = EEXIST;
648 goto msggetout;
1c79356b 649 }
2d21ac55 650 if ((eval = ipcperm(cred, &msqptr->u.msg_perm, msgflg & 0700 ))) {
1c79356b
A
651#ifdef MSG_DEBUG_OK
652 printf("requester doesn't have 0%o access\n",
653 msgflg & 0700);
654#endif
91447636 655 goto msggetout;
1c79356b 656 }
2d21ac55
A
657#if CONFIG_MACF
658 eval = mac_sysvmsq_check_msqget(cred, msqptr);
0a7de745 659 if (eval) {
2d21ac55 660 goto msggetout;
0a7de745 661 }
2d21ac55 662#endif
1c79356b
A
663 goto found;
664 }
665 }
666
667#ifdef MSG_DEBUG_OK
91447636 668 printf("need to allocate the user_msqid_ds\n");
1c79356b
A
669#endif
670 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
671 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
672 /*
91447636
A
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.
1c79356b
A
677 */
678 msqptr = &msqids[msqid];
2d21ac55 679 if (msqptr->u.msg_qbytes == 0 &&
0a7de745 680 (msqptr->u.msg_perm.mode & MSG_LOCKED) == 0) {
1c79356b 681 break;
0a7de745 682 }
1c79356b
A
683 }
684 if (msqid == msginfo.msgmni) {
685#ifdef MSG_DEBUG_OK
91447636 686 printf("no more user_msqid_ds's available\n");
1c79356b 687#endif
91447636
A
688 eval = ENOSPC;
689 goto msggetout;
1c79356b
A
690 }
691#ifdef MSG_DEBUG_OK
692 printf("msqid %d is available\n", msqid);
693#endif
2d21ac55
A
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);
6d2010ae
A
697 msqptr->u.msg_perm.cgid = kauth_cred_getgid(cred);
698 msqptr->u.msg_perm.gid = kauth_cred_getgid(cred);
2d21ac55 699 msqptr->u.msg_perm.mode = (msgflg & 0777);
1c79356b 700 /* Make sure that the returned msqid is unique */
2d21ac55
A
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
1c79356b
A
715 } else {
716#ifdef MSG_DEBUG_OK
717 printf("didn't find it and wasn't asked to create it\n");
718#endif
91447636
A
719 eval = ENOENT;
720 goto msggetout;
1c79356b
A
721 }
722
723found:
724 /* Construct the unique msqid */
2d21ac55 725 *retval = IXSEQ_TO_IPCID(msqid, msqptr->u.msg_perm);
91447636
A
726 AUDIT_ARG(svipc_id, *retval);
727 eval = 0;
728msggetout:
729 SYSV_MSG_SUBSYS_UNLOCK();
0a7de745 730 return eval;
1c79356b
A
731}
732
1c79356b
A
733
734int
b0d623f7 735msgsnd(struct proc *p, struct msgsnd_args *uap, int32_t *retval)
2d21ac55
A
736{
737 __pthread_testcancel(1);
0a7de745 738 return msgsnd_nocancel(p, (struct msgsnd_nocancel_args *)uap, retval);
2d21ac55
A
739}
740
741int
b0d623f7 742msgsnd_nocancel(struct proc *p, struct msgsnd_nocancel_args *uap, int32_t *retval)
1c79356b
A
743{
744 int msqid = uap->msqid;
91447636 745 user_addr_t user_msgp = uap->msgp;
0a7de745 746 size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */
1c79356b
A
747 int msgflg = uap->msgflg;
748 int segs_needed, eval;
2d21ac55 749 struct msqid_kernel *msqptr;
91447636 750 struct msg *msghdr;
1c79356b 751 short next;
91447636
A
752 user_long_t msgtype;
753
754
755 SYSV_MSG_SUBSYS_LOCK();
2d21ac55
A
756
757 if (!msginit(0)) {
758 eval = ENOMEM;
759 goto msgsndout;
760 }
1c79356b
A
761
762#ifdef MSG_DEBUG_OK
b0d623f7 763 printf("call to msgsnd(%d, 0x%qx, %ld, %d)\n", msqid, user_msgp, msgsz,
1c79356b
A
764 msgflg);
765#endif
766
55e303ae 767 AUDIT_ARG(svipc_id, msqid);
1c79356b
A
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
91447636
A
775 eval = EINVAL;
776 goto msgsndout;
1c79356b
A
777 }
778
779 msqptr = &msqids[msqid];
2d21ac55 780 if (msqptr->u.msg_qbytes == 0) {
1c79356b
A
781#ifdef MSG_DEBUG_OK
782 printf("no such message queue id\n");
783#endif
91447636
A
784 eval = EINVAL;
785 goto msgsndout;
1c79356b 786 }
2d21ac55 787 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1c79356b
A
788#ifdef MSG_DEBUG_OK
789 printf("wrong sequence number\n");
790#endif
91447636
A
791 eval = EINVAL;
792 goto msgsndout;
1c79356b
A
793 }
794
2d21ac55 795 if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_W))) {
1c79356b
A
796#ifdef MSG_DEBUG_OK
797 printf("requester doesn't have write access\n");
798#endif
91447636 799 goto msgsndout;
1c79356b
A
800 }
801
2d21ac55
A
802#if CONFIG_MACF
803 eval = mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr);
0a7de745 804 if (eval) {
2d21ac55 805 goto msgsndout;
0a7de745 806 }
2d21ac55 807#endif
1c79356b
A
808 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
809#ifdef MSG_DEBUG_OK
b0d623f7 810 printf("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
1c79356b
A
811 segs_needed);
812#endif
2d21ac55
A
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 */
1c79356b 820 for (;;) {
2d21ac55 821 void *blocking_resource = NULL;
1c79356b
A
822
823 /*
2d21ac55
A
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.
1c79356b 827 */
2d21ac55 828 if (msgsz > msqptr->u.msg_qbytes) {
1c79356b
A
829#ifdef MSG_DEBUG_OK
830 printf("msgsz > msqptr->msg_qbytes\n");
831#endif
91447636
A
832 eval = EINVAL;
833 goto msgsndout;
1c79356b
A
834 }
835
2d21ac55
A
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) {
1c79356b
A
841#ifdef MSG_DEBUG_OK
842 printf("msqid is locked\n");
843#endif
2d21ac55 844 blocking_resource = msqptr;
1c79356b 845 }
2d21ac55
A
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) {
1c79356b
A
853#ifdef MSG_DEBUG_OK
854 printf("msgsz + msg_cbytes > msg_qbytes\n");
855#endif
2d21ac55 856 blocking_resource = msqptr;
1c79356b 857 }
2d21ac55
A
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 */
1c79356b
A
865 if (segs_needed > nfree_msgmaps) {
866#ifdef MSG_DEBUG_OK
867 printf("segs_needed > nfree_msgmaps\n");
868#endif
2d21ac55 869 blocking_resource = &free_msghdrs;
1c79356b
A
870 }
871 if (free_msghdrs == NULL) {
872#ifdef MSG_DEBUG_OK
873 printf("no more msghdrs\n");
874#endif
2d21ac55 875 blocking_resource = &free_msghdrs;
1c79356b
A
876 }
877
2d21ac55 878 if (blocking_resource != NULL) {
1c79356b
A
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
91447636
A
885 eval = EAGAIN;
886 goto msgsndout;
1c79356b
A
887 }
888
2d21ac55 889 if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
1c79356b 890#ifdef MSG_DEBUG_OK
91447636 891 printf("we don't own the user_msqid_ds\n");
1c79356b
A
892#endif
893 we_own_it = 0;
894 } else {
895 /* Force later arrivals to wait for our
0a7de745 896 * request */
1c79356b 897#ifdef MSG_DEBUG_OK
91447636 898 printf("we own the user_msqid_ds\n");
1c79356b 899#endif
2d21ac55 900 msqptr->u.msg_perm.mode |= MSG_LOCKED;
1c79356b
A
901 we_own_it = 1;
902 }
903#ifdef MSG_DEBUG_OK
904 printf("goodnight\n");
905#endif
2d21ac55 906 eval = msleep(blocking_resource, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH,
1c79356b
A
907 "msgwait", 0);
908#ifdef MSG_DEBUG_OK
909 printf("good morning, eval=%d\n", eval);
910#endif
0a7de745 911 if (we_own_it) {
2d21ac55 912 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
0a7de745 913 }
1c79356b
A
914 if (eval != 0) {
915#ifdef MSG_DEBUG_OK
916 printf("msgsnd: interrupted system call\n");
917#endif
91447636
A
918 eval = EINTR;
919 goto msgsndout;
1c79356b
A
920 }
921
922 /*
923 * Make sure that the msq queue still exists
924 */
925
2d21ac55 926 if (msqptr->u.msg_qbytes == 0) {
1c79356b
A
927#ifdef MSG_DEBUG_OK
928 printf("msqid deleted\n");
929#endif
91447636 930 eval = EIDRM;
91447636 931 goto msgsndout;
1c79356b 932 }
1c79356b
A
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
0a7de745 946 if (msqptr->u.msg_perm.mode & MSG_LOCKED) {
1c79356b 947 panic("msg_perm.mode & MSG_LOCKED");
0a7de745
A
948 }
949 if (segs_needed > nfree_msgmaps) {
1c79356b 950 panic("segs_needed > nfree_msgmaps");
0a7de745
A
951 }
952 if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) {
1c79356b 953 panic("msgsz + msg_cbytes > msg_qbytes");
0a7de745
A
954 }
955 if (free_msghdrs == NULL) {
1c79356b 956 panic("no more msghdrs");
0a7de745 957 }
1c79356b
A
958
959 /*
91447636
A
960 * Re-lock the user_msqid_ds in case we page-fault when copying in
961 * the message
1c79356b 962 */
0a7de745 963 if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
91447636 964 panic("user_msqid_ds is already locked");
0a7de745 965 }
2d21ac55 966 msqptr->u.msg_perm.mode |= MSG_LOCKED;
1c79356b
A
967
968 /*
969 * Allocate a message header
970 */
1c79356b
A
971 msghdr = free_msghdrs;
972 free_msghdrs = msghdr->msg_next;
973 msghdr->msg_spot = -1;
974 msghdr->msg_ts = msgsz;
975
2d21ac55
A
976#if CONFIG_MACF
977 mac_sysvmsg_label_associate(kauth_cred_get(), msqptr, msghdr);
978#endif
1c79356b
A
979 /*
980 * Allocate space for the message
981 */
982
983 while (segs_needed > 0) {
0a7de745 984 if (nfree_msgmaps <= 0) {
1c79356b 985 panic("not enough msgmaps");
0a7de745
A
986 }
987 if (free_msgmaps == -1) {
1c79356b 988 panic("nil free_msgmaps");
0a7de745 989 }
1c79356b 990 next = free_msgmaps;
0a7de745 991 if (next <= -1) {
1c79356b 992 panic("next too low #1");
0a7de745
A
993 }
994 if (next >= msginfo.msgseg) {
1c79356b 995 panic("next out of range #1");
0a7de745 996 }
1c79356b
A
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 /*
91447636
A
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.
1c79356b 1010 */
91447636
A
1011 if (IS_64BIT_PROCESS(p)) {
1012 SYSV_MSG_SUBSYS_UNLOCK();
1013 eval = copyin(user_msgp, &msgtype, sizeof(msgtype));
1014 SYSV_MSG_SUBSYS_LOCK();
0a7de745
A
1015 msghdr->msg_type = CAST_DOWN(long, msgtype);
1016 user_msgp = user_msgp + sizeof(msgtype); /* ptr math */
91447636
A
1017 } else {
1018 SYSV_MSG_SUBSYS_UNLOCK();
b0d623f7
A
1019 int32_t msg_type32;
1020 eval = copyin(user_msgp, &msg_type32, sizeof(msg_type32));
1021 msghdr->msg_type = msg_type32;
91447636 1022 SYSV_MSG_SUBSYS_LOCK();
0a7de745 1023 user_msgp = user_msgp + sizeof(msg_type32); /* ptr math */
91447636 1024 }
1c79356b 1025
91447636 1026 if (eval != 0) {
1c79356b
A
1027#ifdef MSG_DEBUG_OK
1028 printf("error %d copying the message type\n", eval);
1029#endif
1030 msg_freehdr(msghdr);
2d21ac55 1031 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1c79356b 1032 wakeup((caddr_t)msqptr);
91447636 1033 goto msgsndout;
1c79356b 1034 }
91447636 1035
1c79356b
A
1036
1037 /*
1038 * Validate the message type
1039 */
1c79356b
A
1040 if (msghdr->msg_type < 1) {
1041 msg_freehdr(msghdr);
2d21ac55 1042 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1c79356b
A
1043 wakeup((caddr_t)msqptr);
1044#ifdef MSG_DEBUG_OK
b0d623f7 1045 printf("mtype (%ld) < 1\n", msghdr->msg_type);
1c79356b 1046#endif
91447636
A
1047 eval = EINVAL;
1048 goto msgsndout;
1c79356b
A
1049 }
1050
1051 /*
1052 * Copy in the message body
1053 */
1c79356b
A
1054 next = msghdr->msg_spot;
1055 while (msgsz > 0) {
1056 size_t tlen;
91447636 1057 /* compare input (size_t) value against restrict (int) value */
0a7de745 1058 if (msgsz > (size_t)msginfo.msgssz) {
1c79356b 1059 tlen = msginfo.msgssz;
0a7de745 1060 } else {
1c79356b 1061 tlen = msgsz;
0a7de745
A
1062 }
1063 if (next <= -1) {
1c79356b 1064 panic("next too low #2");
0a7de745
A
1065 }
1066 if (next >= msginfo.msgseg) {
1c79356b 1067 panic("next out of range #2");
0a7de745 1068 }
91447636
A
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) {
1c79356b
A
1075#ifdef MSG_DEBUG_OK
1076 printf("error %d copying in message segment\n", eval);
1077#endif
1078 msg_freehdr(msghdr);
2d21ac55 1079 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1c79356b 1080 wakeup((caddr_t)msqptr);
91447636
A
1081
1082 goto msgsndout;
1c79356b
A
1083 }
1084 msgsz -= tlen;
0a7de745 1085 user_msgp = user_msgp + tlen; /* ptr math */
1c79356b
A
1086 next = msgmaps[next].next;
1087 }
0a7de745 1088 if (next != -1) {
1c79356b 1089 panic("didn't use all the msg segments");
0a7de745 1090 }
1c79356b
A
1091
1092 /*
91447636 1093 * We've got the message. Unlock the user_msqid_ds.
1c79356b
A
1094 */
1095
2d21ac55 1096 msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
1c79356b
A
1097
1098 /*
91447636 1099 * Make sure that the user_msqid_ds is still allocated.
1c79356b
A
1100 */
1101
2d21ac55 1102 if (msqptr->u.msg_qbytes == 0) {
1c79356b
A
1103 msg_freehdr(msghdr);
1104 wakeup((caddr_t)msqptr);
1105 /* The SVID says to return EIDRM. */
1106#ifdef EIDRM
91447636 1107 eval = EIDRM;
1c79356b
A
1108#else
1109 /* Unfortunately, BSD doesn't define that code yet! */
91447636 1110 eval = EINVAL;
1c79356b 1111#endif
91447636 1112 goto msgsndout;
1c79356b
A
1113 }
1114
2d21ac55
A
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
1c79356b
A
1134 /*
1135 * Put the message into the queue
1136 */
1137
2d21ac55
A
1138 if (msqptr->u.msg_first == NULL) {
1139 msqptr->u.msg_first = msghdr;
1140 msqptr->u.msg_last = msghdr;
1c79356b 1141 } else {
2d21ac55
A
1142 msqptr->u.msg_last->msg_next = msghdr;
1143 msqptr->u.msg_last = msghdr;
1c79356b 1144 }
2d21ac55 1145 msqptr->u.msg_last->msg_next = NULL;
1c79356b 1146
2d21ac55
A
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();
1c79356b
A
1151
1152 wakeup((caddr_t)msqptr);
91447636
A
1153 *retval = 0;
1154 eval = 0;
1155
1156msgsndout:
1157 SYSV_MSG_SUBSYS_UNLOCK();
0a7de745 1158 return eval;
1c79356b
A
1159}
1160
1c79356b
A
1161
1162int
91447636 1163msgrcv(struct proc *p, struct msgrcv_args *uap, user_ssize_t *retval)
2d21ac55
A
1164{
1165 __pthread_testcancel(1);
0a7de745 1166 return msgrcv_nocancel(p, (struct msgrcv_nocancel_args *)uap, retval);
2d21ac55
A
1167}
1168
1169int
1170msgrcv_nocancel(struct proc *p, struct msgrcv_nocancel_args *uap, user_ssize_t *retval)
1c79356b
A
1171{
1172 int msqid = uap->msqid;
91447636 1173 user_addr_t user_msgp = uap->msgp;
0a7de745
A
1174 size_t msgsz = (size_t)uap->msgsz; /* limit to 4G */
1175 long msgtyp = (long)uap->msgtyp; /* limit to 32 bits */
1c79356b
A
1176 int msgflg = uap->msgflg;
1177 size_t len;
2d21ac55 1178 struct msqid_kernel *msqptr;
91447636 1179 struct msg *msghdr;
1c79356b
A
1180 int eval;
1181 short next;
91447636 1182 user_long_t msgtype;
b0d623f7 1183 int32_t msg_type32;
91447636
A
1184
1185 SYSV_MSG_SUBSYS_LOCK();
2d21ac55
A
1186
1187 if (!msginit(0)) {
1188 eval = ENOMEM;
1189 goto msgrcvout;
1190 }
1c79356b
A
1191
1192#ifdef MSG_DEBUG_OK
b0d623f7 1193 printf("call to msgrcv(%d, 0x%qx, %ld, %ld, %d)\n", msqid, user_msgp,
1c79356b
A
1194 msgsz, msgtyp, msgflg);
1195#endif
1196
55e303ae 1197 AUDIT_ARG(svipc_id, msqid);
1c79356b
A
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
91447636
A
1205 eval = EINVAL;
1206 goto msgrcvout;
1c79356b
A
1207 }
1208
1209 msqptr = &msqids[msqid];
2d21ac55 1210 if (msqptr->u.msg_qbytes == 0) {
1c79356b
A
1211#ifdef MSG_DEBUG_OK
1212 printf("no such message queue id\n");
1213#endif
91447636
A
1214 eval = EINVAL;
1215 goto msgrcvout;
1c79356b 1216 }
2d21ac55 1217 if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1c79356b
A
1218#ifdef MSG_DEBUG_OK
1219 printf("wrong sequence number\n");
1220#endif
91447636
A
1221 eval = EINVAL;
1222 goto msgrcvout;
1c79356b
A
1223 }
1224
2d21ac55 1225 if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_R))) {
1c79356b
A
1226#ifdef MSG_DEBUG_OK
1227 printf("requester doesn't have read access\n");
1228#endif
91447636 1229 goto msgrcvout;
1c79356b
A
1230 }
1231
2d21ac55
A
1232#if CONFIG_MACF
1233 eval = mac_sysvmsq_check_msqrcv(kauth_cred_get(), msqptr);
0a7de745 1234 if (eval) {
2d21ac55 1235 goto msgrcvout;
0a7de745 1236 }
2d21ac55 1237#endif
1c79356b
A
1238 msghdr = NULL;
1239 while (msghdr == NULL) {
1240 if (msgtyp == 0) {
2d21ac55 1241 msghdr = msqptr->u.msg_first;
1c79356b
A
1242 if (msghdr != NULL) {
1243 if (msgsz < msghdr->msg_ts &&
1244 (msgflg & MSG_NOERROR) == 0) {
1245#ifdef MSG_DEBUG_OK
b0d623f7 1246 printf("first message on the queue is too big (want %ld, got %d)\n",
1c79356b
A
1247 msgsz, msghdr->msg_ts);
1248#endif
91447636
A
1249 eval = E2BIG;
1250 goto msgrcvout;
1c79356b 1251 }
2d21ac55
A
1252#if CONFIG_MACF
1253 eval = mac_sysvmsq_check_msgrcv(kauth_cred_get(),
1254 msghdr);
0a7de745 1255 if (eval) {
2d21ac55 1256 goto msgrcvout;
0a7de745 1257 }
2d21ac55
A
1258#endif
1259 if (msqptr->u.msg_first == msqptr->u.msg_last) {
1260 msqptr->u.msg_first = NULL;
1261 msqptr->u.msg_last = NULL;
1c79356b 1262 } else {
2d21ac55 1263 msqptr->u.msg_first = msghdr->msg_next;
0a7de745 1264 if (msqptr->u.msg_first == NULL) {
1c79356b 1265 panic("msg_first/last messed up #1");
0a7de745 1266 }
1c79356b
A
1267 }
1268 }
1269 } else {
1270 struct msg *previous;
1271 struct msg **prev;
1272
1273 previous = NULL;
2d21ac55 1274 prev = &(msqptr->u.msg_first);
1c79356b
A
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
b0d623f7 1288 printf("found message type %ld, requested %ld\n",
1c79356b
A
1289 msghdr->msg_type, msgtyp);
1290#endif
1291 if (msgsz < msghdr->msg_ts &&
1292 (msgflg & MSG_NOERROR) == 0) {
1293#ifdef MSG_DEBUG_OK
b0d623f7 1294 printf("requested message on the queue is too big (want %ld, got %d)\n",
1c79356b
A
1295 msgsz, msghdr->msg_ts);
1296#endif
91447636
A
1297 eval = E2BIG;
1298 goto msgrcvout;
1c79356b 1299 }
2d21ac55
A
1300#if CONFIG_MACF
1301 eval = mac_sysvmsq_check_msgrcv(
0a7de745
A
1302 kauth_cred_get(), msghdr);
1303 if (eval) {
2d21ac55 1304 goto msgrcvout;
0a7de745 1305 }
2d21ac55 1306#endif
1c79356b 1307 *prev = msghdr->msg_next;
2d21ac55 1308 if (msghdr == msqptr->u.msg_last) {
1c79356b
A
1309 if (previous == NULL) {
1310 if (prev !=
0a7de745 1311 &msqptr->u.msg_first) {
1c79356b 1312 panic("msg_first/last messed up #2");
0a7de745 1313 }
2d21ac55 1314 msqptr->u.msg_first =
1c79356b 1315 NULL;
2d21ac55 1316 msqptr->u.msg_last =
1c79356b
A
1317 NULL;
1318 } else {
1319 if (prev ==
0a7de745 1320 &msqptr->u.msg_first) {
1c79356b 1321 panic("msg_first/last messed up #3");
0a7de745 1322 }
2d21ac55 1323 msqptr->u.msg_last =
1c79356b
A
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
0a7de745 1340 if (msghdr != NULL) {
1c79356b 1341 break;
0a7de745 1342 }
1c79356b
A
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
b0d623f7 1350 printf("no appropriate message found (msgtyp=%ld)\n",
1c79356b
A
1351 msgtyp);
1352#endif
1353 /* The SVID says to return ENOMSG. */
1354#ifdef ENOMSG
91447636 1355 eval = ENOMSG;
1c79356b
A
1356#else
1357 /* Unfortunately, BSD doesn't define that code yet! */
91447636 1358 eval = EAGAIN;
1c79356b 1359#endif
91447636 1360 goto msgrcvout;
1c79356b
A
1361 }
1362
1363 /*
1364 * Wait for something to happen
1365 */
1366
1367#ifdef MSG_DEBUG_OK
1368 printf("msgrcv: goodnight\n");
1369#endif
91447636 1370 eval = msleep((caddr_t)msqptr, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH, "msgwait",
1c79356b
A
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
91447636
A
1380 eval = EINTR;
1381 goto msgrcvout;
1c79356b
A
1382 }
1383
1384 /*
1385 * Make sure that the msq queue still exists
1386 */
1387
2d21ac55
A
1388 if (msqptr->u.msg_qbytes == 0 ||
1389 msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
1c79356b
A
1390#ifdef MSG_DEBUG_OK
1391 printf("msqid deleted\n");
1392#endif
1393 /* The SVID says to return EIDRM. */
1394#ifdef EIDRM
91447636 1395 eval = EIDRM;
1c79356b
A
1396#else
1397 /* Unfortunately, BSD doesn't define that code yet! */
91447636 1398 eval = EINVAL;
1c79356b 1399#endif
91447636 1400 goto msgrcvout;
1c79356b
A
1401 }
1402 }
1403
1404 /*
1405 * Return the message to the user.
1406 *
1407 * First, do the bookkeeping (before we risk being interrupted).
1408 */
1409
2d21ac55
A
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();
1c79356b
A
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
b0d623f7 1422 printf("found a message, msgsz=%ld, msg_ts=%d\n", msgsz,
1c79356b
A
1423 msghdr->msg_ts);
1424#endif
0a7de745 1425 if (msgsz > msghdr->msg_ts) {
1c79356b 1426 msgsz = msghdr->msg_ts;
0a7de745 1427 }
1c79356b
A
1428
1429 /*
1430 * Return the type to the user.
1431 */
1432
91447636
A
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();
0a7de745 1442 user_msgp = user_msgp + sizeof(msgtype); /* ptr math */
91447636 1443 } else {
b0d623f7 1444 msg_type32 = msghdr->msg_type;
91447636 1445 SYSV_MSG_SUBSYS_UNLOCK();
b0d623f7 1446 eval = copyout(&msg_type32, user_msgp, sizeof(msg_type32));
91447636 1447 SYSV_MSG_SUBSYS_LOCK();
0a7de745 1448 user_msgp = user_msgp + sizeof(msg_type32); /* ptr math */
91447636
A
1449 }
1450
1c79356b
A
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);
91447636
A
1457
1458 goto msgrcvout;
1c79356b 1459 }
91447636 1460
1c79356b
A
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
91447636 1470 /* compare input (size_t) value against restrict (int) value */
0a7de745 1471 if (msgsz > (size_t)msginfo.msgssz) {
1c79356b 1472 tlen = msginfo.msgssz;
0a7de745 1473 } else {
1c79356b 1474 tlen = msgsz;
0a7de745
A
1475 }
1476 if (next <= -1) {
1c79356b 1477 panic("next too low #3");
0a7de745
A
1478 }
1479 if (next >= msginfo.msgseg) {
1c79356b 1480 panic("next out of range #3");
0a7de745 1481 }
91447636
A
1482 SYSV_MSG_SUBSYS_UNLOCK();
1483 eval = copyout(&msgpool[next * msginfo.msgssz],
1c79356b 1484 user_msgp, tlen);
91447636 1485 SYSV_MSG_SUBSYS_LOCK();
1c79356b
A
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);
91447636 1493 goto msgrcvout;
1c79356b 1494 }
0a7de745 1495 user_msgp = user_msgp + tlen; /* ptr math */
1c79356b
A
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);
91447636
A
1505 *retval = msgsz;
1506 eval = 0;
1507msgrcvout:
1508 SYSV_MSG_SUBSYS_UNLOCK();
0a7de745 1509 return eval;
91447636
A
1510}
1511
1512static int
1513IPCS_msg_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
0a7de745 1514 __unused int arg2, struct sysctl_req *req)
91447636
A
1515{
1516 int error;
1517 int cursor;
1518 union {
b0d623f7 1519 struct user32_IPCS_command u32;
91447636
A
1520 struct user_IPCS_command u64;
1521 } ipcs;
0a7de745
A
1522 struct user32_msqid_ds msqid_ds32 = {}; /* post conversion, 32 bit version */
1523 struct user64_msqid_ds msqid_ds64 = {}; /* post conversion, 64 bit version */
91447636 1524 void *msqid_dsp;
b0d623f7
A
1525 size_t ipcs_sz;
1526 size_t msqid_ds_sz;
91447636
A
1527 struct proc *p = current_proc();
1528
b0d623f7
A
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);
91447636
A
1535 }
1536
1537 /* Copy in the command structure */
1538 if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) {
0a7de745 1539 return error;
91447636
A
1540 }
1541
0a7de745 1542 if (!IS_64BIT_PROCESS(p)) { /* convert in place */
91447636 1543 ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data);
0a7de745 1544 }
91447636
A
1545
1546 /* Let us version this interface... */
1547 if (ipcs.u64.ipcs_magic != IPCS_MAGIC) {
0a7de745 1548 return EINVAL;
91447636
A
1549 }
1550
1551 SYSV_MSG_SUBSYS_LOCK();
1552
0a7de745
A
1553 switch (ipcs.u64.ipcs_op) {
1554 case IPCS_MSG_CONF: /* Obtain global configuration data */
91447636
A
1555 if (ipcs.u64.ipcs_datalen != sizeof(struct msginfo)) {
1556 error = ERANGE;
1557 break;
1558 }
0a7de745 1559 if (ipcs.u64.ipcs_cursor != 0) { /* fwd. compat. */
91447636
A
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
0a7de745 1568 case IPCS_MSG_ITER: /* Iterate over existing segments */
91447636 1569 /* Not done up top so we can set limits via sysctl (later) */
2d21ac55
A
1570 if (!msginit(0)) {
1571 error = ENOMEM;
1572 break;
1573 }
91447636
A
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) {
2d21ac55 1581 error = EINVAL;
91447636
A
1582 break;
1583 }
0a7de745
A
1584 for (; cursor < msginfo.msgmni; cursor++) {
1585 if (msqids[cursor].u.msg_qbytes != 0) { /* allocated */
91447636 1586 break;
0a7de745 1587 }
91447636
A
1588 continue;
1589 }
1590 if (cursor == msginfo.msgmni) {
1591 error = ENOENT;
1592 break;
1593 }
1594
0a7de745 1595 msqid_dsp = &msqids[cursor]; /* default: 64 bit */
91447636
A
1596
1597 /*
1598 * If necessary, convert the 64 bit kernel segment
1599 * descriptor to a 32 bit user one.
1600 */
b0d623f7
A
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);
91447636
A
1606 msqid_dsp = &msqid_ds32;
1607 }
b0d623f7 1608
91447636
A
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
0a7de745
A
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 }
91447636
A
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();
0a7de745 1629 return error;
1c79356b 1630}
91447636
A
1631
1632SYSCTL_DECL(_kern_sysv_ipcs);
6d2010ae 1633SYSCTL_PROC(_kern_sysv_ipcs, OID_AUTO, msg, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
0a7de745
A
1634 0, 0, IPCS_msg_sysctl,
1635 "S,IPCS_msg_command",
1636 "ipcs msg command interface");
2d21ac55
A
1637
1638#endif /* SYSV_MSG */