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 * @OSF_FREE_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or [email protected]
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56 /*
57 */
58 /*
59 * File: ipc/ipc_mqueue.c
60 * Author: Rich Draves
61 * Date: 1989
62 *
63 * Functions to manipulate IPC message queues.
64 */
65 /*
66 * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce
67 * support for mandatory and extensible security protections. This notice
68 * is included in support of clause 2.2 (b) of the Apple Public License,
69 * Version 2.0.
70 */
71
72
73 #include <mach/port.h>
74 #include <mach/message.h>
75 #include <mach/sync_policy.h>
76
77 #include <kern/assert.h>
78 #include <kern/counter.h>
79 #include <kern/sched_prim.h>
80 #include <kern/ipc_kobject.h>
81 #include <kern/ipc_mig.h> /* XXX - for mach_msg_receive_continue */
82 #include <kern/misc_protos.h>
83 #include <kern/task.h>
84 #include <kern/thread.h>
85 #include <kern/waitq.h>
86
87 #include <ipc/port.h>
88 #include <ipc/ipc_mqueue.h>
89 #include <ipc/ipc_kmsg.h>
90 #include <ipc/ipc_right.h>
91 #include <ipc/ipc_port.h>
92 #include <ipc/ipc_pset.h>
93 #include <ipc/ipc_space.h>
94
95 #if MACH_FLIPC
96 #include <ipc/flipc.h>
97 #endif
98
99 #ifdef __LP64__
100 #include <vm/vm_map.h>
101 #endif
102
103 #include <sys/event.h>
104
105 extern char *proc_name_address(void *p);
106
107 int ipc_mqueue_full; /* address is event for queue space */
108 int ipc_mqueue_rcv; /* address is event for message arrival */
109
110 /* forward declarations */
111 static void ipc_mqueue_receive_results(wait_result_t result);
112 static void ipc_mqueue_peek_on_thread_locked(
113 ipc_mqueue_t port_mq,
114 mach_msg_option64_t option,
115 thread_t thread);
116
117 /* Deliver message to message queue or waiting receiver */
118 static void ipc_mqueue_post(
119 ipc_mqueue_t mqueue,
120 ipc_kmsg_t kmsg,
121 mach_msg_option_t option);
122
123 /*
124 * Routine: ipc_mqueue_init
125 * Purpose:
126 * Initialize a newly-allocated message queue.
127 */
128 void
ipc_mqueue_init(ipc_mqueue_t mqueue)129 ipc_mqueue_init(
130 ipc_mqueue_t mqueue)
131 {
132 ipc_kmsg_queue_init(&mqueue->imq_messages);
133 mqueue->imq_qlimit = MACH_PORT_QLIMIT_DEFAULT;
134 klist_init(&mqueue->imq_klist);
135 }
136
137 /*
138 * Routine: ipc_mqueue_add_locked.
139 * Purpose:
140 * Associate the portset's mqueue with the port's mqueue.
141 * This has to be done so that posting the port will wakeup
142 * a portset waiter. If there are waiters on the portset
143 * mqueue and messages on the port mqueue, try to match them
144 * up now.
145 * Conditions:
146 * Port and Pset both locked.
147 */
148 kern_return_t
ipc_mqueue_add_locked(ipc_mqueue_t port_mqueue,ipc_pset_t pset,waitq_link_t * linkp)149 ipc_mqueue_add_locked(
150 ipc_mqueue_t port_mqueue,
151 ipc_pset_t pset,
152 waitq_link_t *linkp)
153 {
154 ipc_port_t port = ip_from_mq(port_mqueue);
155 struct waitq_set *wqset = &pset->ips_wqset;
156 circle_queue_t kmsgq = &port_mqueue->imq_messages;
157 kern_return_t kr = KERN_SUCCESS;
158 ipc_kmsg_t kmsg;
159
160 kr = waitq_link_locked(&port->ip_waitq, wqset, linkp);
161 if (kr != KERN_SUCCESS) {
162 return kr;
163 }
164
165 /*
166 * Now that the set has been added to the port, there may be
167 * messages queued on the port and threads waiting on the set
168 * waitq. Lets get them together.
169 *
170 * Only consider this set however, as the other ones have been
171 * posted to already.
172 */
173 while ((kmsg = ipc_kmsg_queue_first(kmsgq)) != IKM_NULL) {
174 thread_t th;
175 mach_msg_size_t msize, asize;
176 spl_t th_spl;
177
178 th = waitq_wakeup64_identify_locked(wqset, IPC_MQUEUE_RECEIVE,
179 THREAD_AWAKENED, WAITQ_KEEP_LOCKED, &th_spl);
180 /* port and pset still locked, thread locked */
181
182 if (th == THREAD_NULL) {
183 /*
184 * Didn't find a thread to wake up but messages
185 * are enqueued, prepost the set instead,
186 * as calling waitq_wakeup64_identify_locked()
187 * on the set directly will not take care of it.
188 */
189 waitq_link_prepost_locked(&port->ip_waitq, wqset);
190 break;
191 }
192
193 /*
194 * If the receiver waited with a facility not directly
195 * related to Mach messaging, then it isn't prepared to get
196 * handed the message directly. Just set it running, and
197 * go look for another thread that can.
198 */
199 if (th->ith_state != MACH_RCV_IN_PROGRESS) {
200 if (th->ith_state == MACH_PEEK_IN_PROGRESS) {
201 /*
202 * wakeup the peeking thread, but
203 * continue to loop over the threads
204 * waiting on the port's mqueue to see
205 * if there are any actual receivers
206 */
207 ipc_mqueue_peek_on_thread_locked(port_mqueue,
208 th->ith_option,
209 th);
210 }
211 thread_unlock(th);
212 splx(th_spl);
213 continue;
214 }
215
216 /*
217 * Found a receiver. see if they can handle the message
218 * correctly (the message is not too large for them, or
219 * they didn't care to be informed that the message was
220 * too large). If they can't handle it, take them off
221 * the list and let them go back and figure it out and
222 * just move onto the next.
223 */
224 msize = ipc_kmsg_copyout_size(kmsg, th->map);
225 asize = ipc_kmsg_aux_data_size(kmsg);
226
227 if (ipc_kmsg_too_large(msize, asize, th->ith_option,
228 th->ith_max_msize, th->ith_max_asize, th)) {
229 th->ith_state = MACH_RCV_TOO_LARGE;
230 th->ith_msize = msize;
231 th->ith_asize = asize;
232 if (th->ith_option & MACH_RCV_LARGE) {
233 /*
234 * let him go without message
235 */
236 th->ith_receiver_name = port_mqueue->imq_receiver_name;
237 th->ith_kmsg = IKM_NULL;
238 th->ith_seqno = 0;
239 thread_unlock(th);
240 splx(th_spl);
241 continue; /* find another thread */
242 }
243 } else {
244 th->ith_state = MACH_MSG_SUCCESS;
245 }
246
247 /*
248 * This thread is going to take this message,
249 * so give it to him.
250 */
251 ipc_kmsg_rmqueue(kmsgq, kmsg);
252 #if MACH_FLIPC
253 mach_node_t node = kmsg->ikm_node;
254 #endif
255 ipc_mqueue_release_msgcount(port_mqueue);
256
257 th->ith_kmsg = kmsg;
258 th->ith_seqno = port_mqueue->imq_seqno++;
259 thread_unlock(th);
260 splx(th_spl);
261 #if MACH_FLIPC
262 if (MACH_NODE_VALID(node) && FPORT_VALID(port_mqueue->imq_fport)) {
263 flipc_msg_ack(node, port_mqueue, TRUE);
264 }
265 #endif
266 }
267
268 return KERN_SUCCESS;
269 }
270
271
272 /*
273 * Routine: ipc_port_has_klist
274 * Purpose:
275 * Returns whether the given port imq_klist field can be used as a klist.
276 */
277 bool
ipc_port_has_klist(ipc_port_t port)278 ipc_port_has_klist(ipc_port_t port)
279 {
280 return !port->ip_specialreply &&
281 port->ip_sync_link_state == PORT_SYNC_LINK_ANY;
282 }
283
284 static inline struct klist *
ipc_object_klist(ipc_object_t object)285 ipc_object_klist(ipc_object_t object)
286 {
287 if (io_otype(object) == IOT_PORT) {
288 ipc_port_t port = ip_object_to_port(object);
289
290 return ipc_port_has_klist(port) ? &port->ip_klist : NULL;
291 }
292 return &ips_object_to_pset(object)->ips_klist;
293 }
294
295 /*
296 * Routine: ipc_mqueue_changed
297 * Purpose:
298 * Wake up receivers waiting in a message queue.
299 * Conditions:
300 * The object containing the message queue is locked.
301 */
302 void
ipc_mqueue_changed(ipc_space_t space,struct waitq * waitq)303 ipc_mqueue_changed(
304 ipc_space_t space,
305 struct waitq *waitq)
306 {
307 ipc_object_t object = io_from_waitq(waitq);
308 struct klist *klist = ipc_object_klist(object);
309
310 if (klist && SLIST_FIRST(klist)) {
311 /*
312 * Indicate that this message queue is vanishing
313 *
314 * When this is called, the associated receive right may be in flight
315 * between two tasks: the one it used to live in, and the one that armed
316 * a port destroyed notification for it.
317 *
318 * The new process may want to register the port it gets back with an
319 * EVFILT_MACHPORT filter again, and may have pending sync IPC on this
320 * port pending already, in which case we want the imq_klist field to be
321 * reusable for nefarious purposes.
322 *
323 * Fortunately, we really don't need this linkage anymore after this
324 * point as EV_VANISHED / EV_EOF will be the last thing delivered ever.
325 *
326 * Note: we don't have the space lock here, however, this covers the
327 * case of when a task is terminating the space, triggering
328 * several knote_vanish() calls.
329 *
330 * We don't need the lock to observe that the space is inactive as
331 * we just deactivated it on the same thread.
332 *
333 * We still need to call knote_vanish() so that the knote is
334 * marked with EV_VANISHED or EV_EOF so that the detach step
335 * in filt_machportdetach is skipped correctly.
336 */
337 assert(space);
338 knote_vanish(klist, is_active(space));
339 }
340
341 if (io_otype(object) == IOT_PORT) {
342 ipc_port_adjust_sync_link_state_locked(ip_object_to_port(object),
343 PORT_SYNC_LINK_ANY, NULL);
344 } else {
345 klist_init(klist);
346 }
347
348 /*
349 * do not pass WAITQ_UPDATE_INHERITOR, ipc_port_destroy()
350 * needs to handle this manually, and the port lock
351 * is the waitq lock, so there's really no inefficiency there.
352 */
353 waitq_wakeup64_all_locked(waitq, IPC_MQUEUE_RECEIVE,
354 THREAD_RESTART, WAITQ_KEEP_LOCKED);
355 }
356
357
358
359
360 /*
361 * Routine: ipc_mqueue_send
362 * Purpose:
363 * Send a message to a message queue. The message holds a reference
364 * for the destination port for this message queue in the
365 * msgh_remote_port field.
366 *
367 * If unsuccessful, the caller still has possession of
368 * the message and must do something with it. If successful,
369 * the message is queued, given to a receiver, or destroyed.
370 * Conditions:
371 * port is locked.
372 * Returns:
373 * MACH_MSG_SUCCESS The message was accepted.
374 * MACH_SEND_TIMED_OUT Caller still has message.
375 * MACH_SEND_INTERRUPTED Caller still has message.
376 */
377 mach_msg_return_t
ipc_mqueue_send_locked(ipc_mqueue_t mqueue,ipc_kmsg_t kmsg,mach_msg_option_t option,mach_msg_timeout_t send_timeout)378 ipc_mqueue_send_locked(
379 ipc_mqueue_t mqueue,
380 ipc_kmsg_t kmsg,
381 mach_msg_option_t option,
382 mach_msg_timeout_t send_timeout)
383 {
384 ipc_port_t port = ip_from_mq(mqueue);
385 int wresult;
386
387 /*
388 * Don't block if:
389 * 1) We're under the queue limit.
390 * 2) Caller used the MACH_SEND_ALWAYS internal option.
391 * 3) Message is sent to a send-once right.
392 */
393 if (!imq_full(mqueue) ||
394 (!imq_full_kernel(mqueue) &&
395 ((option & MACH_SEND_ALWAYS) ||
396 (MACH_MSGH_BITS_REMOTE(ikm_header(kmsg)->msgh_bits) ==
397 MACH_MSG_TYPE_PORT_SEND_ONCE)))) {
398 mqueue->imq_msgcount++;
399 assert(mqueue->imq_msgcount > 0);
400 ip_mq_unlock(port);
401 } else {
402 thread_t cur_thread = current_thread();
403 struct turnstile *send_turnstile = TURNSTILE_NULL;
404 uint64_t deadline;
405
406 /*
407 * We have to wait for space to be granted to us.
408 */
409 if ((option & MACH_SEND_TIMEOUT) && (send_timeout == 0)) {
410 ip_mq_unlock(port);
411 return MACH_SEND_TIMED_OUT;
412 }
413 if (imq_full_kernel(mqueue)) {
414 ip_mq_unlock(port);
415 return MACH_SEND_NO_BUFFER;
416 }
417 port->ip_fullwaiters = true;
418
419 if (option & MACH_SEND_TIMEOUT) {
420 clock_interval_to_deadline(send_timeout, 1000 * NSEC_PER_USEC, &deadline);
421 } else {
422 deadline = 0;
423 }
424
425 thread_set_pending_block_hint(cur_thread, kThreadWaitPortSend);
426
427 send_turnstile = turnstile_prepare((uintptr_t)port,
428 port_send_turnstile_address(port),
429 TURNSTILE_NULL, TURNSTILE_SYNC_IPC);
430
431 ipc_port_send_update_inheritor(port, send_turnstile,
432 TURNSTILE_DELAYED_UPDATE);
433
434 wresult = waitq_assert_wait64_leeway(
435 &send_turnstile->ts_waitq,
436 IPC_MQUEUE_FULL,
437 THREAD_ABORTSAFE,
438 TIMEOUT_URGENCY_USER_NORMAL,
439 deadline,
440 TIMEOUT_NO_LEEWAY);
441
442 ip_mq_unlock(port);
443 turnstile_update_inheritor_complete(send_turnstile,
444 TURNSTILE_INTERLOCK_NOT_HELD);
445
446 if (wresult == THREAD_WAITING) {
447 wresult = thread_block(THREAD_CONTINUE_NULL);
448 }
449
450 /* Call turnstile complete with interlock held */
451 ip_mq_lock(port);
452 turnstile_complete((uintptr_t)port, port_send_turnstile_address(port), NULL, TURNSTILE_SYNC_IPC);
453 ip_mq_unlock(port);
454
455 /* Call cleanup after dropping the interlock */
456 turnstile_cleanup();
457
458 switch (wresult) {
459 case THREAD_AWAKENED:
460 /*
461 * we can proceed - inherited msgcount from waker
462 * or the message queue has been destroyed and the msgcount
463 * has been reset to zero (will detect in ipc_mqueue_post()).
464 */
465 break;
466
467 case THREAD_TIMED_OUT:
468 assert(option & MACH_SEND_TIMEOUT);
469 return MACH_SEND_TIMED_OUT;
470
471 case THREAD_INTERRUPTED:
472 return MACH_SEND_INTERRUPTED;
473
474 case THREAD_RESTART:
475 /* mqueue is being destroyed */
476 return MACH_SEND_INVALID_DEST;
477 default:
478 panic("ipc_mqueue_send");
479 }
480 }
481
482 ipc_mqueue_post(mqueue, kmsg, option);
483 return MACH_MSG_SUCCESS;
484 }
485
486 /*
487 * Routine: ipc_mqueue_override_send_locked
488 * Purpose:
489 * Set an override qos on the first message in the queue
490 * (if the queue is full). This is a send-possible override
491 * that will go away as soon as we drain a message from the
492 * queue.
493 *
494 * Conditions:
495 * The port corresponding to mqueue is locked.
496 * The caller holds a reference on the message queue.
497 */
498 void
ipc_mqueue_override_send_locked(ipc_mqueue_t mqueue,mach_msg_qos_t qos_ovr)499 ipc_mqueue_override_send_locked(
500 ipc_mqueue_t mqueue,
501 mach_msg_qos_t qos_ovr)
502 {
503 ipc_port_t port = ip_from_mq(mqueue);
504
505 assert(waitq_is_valid(&port->ip_waitq));
506
507 if (imq_full(mqueue)) {
508 ipc_kmsg_t first = ipc_kmsg_queue_first(&mqueue->imq_messages);
509
510 if (first && ipc_kmsg_override_qos(&mqueue->imq_messages, first, qos_ovr)) {
511 if (ip_in_a_space(port) &&
512 is_active(ip_get_receiver(port)) &&
513 ipc_port_has_klist(port)) {
514 KNOTE(&port->ip_klist, 0);
515 }
516 }
517 }
518 }
519
520 /*
521 * Routine: ipc_mqueue_release_msgcount
522 * Purpose:
523 * Release a message queue reference in the case where we
524 * found a waiter.
525 *
526 * Conditions:
527 * The port corresponding to message queue is locked.
528 * The message corresponding to this reference is off the queue.
529 * There is no need to pass reserved preposts because this will
530 * never prepost to anyone
531 */
532 void
ipc_mqueue_release_msgcount(ipc_mqueue_t port_mq)533 ipc_mqueue_release_msgcount(ipc_mqueue_t port_mq)
534 {
535 ipc_port_t port = ip_from_mq(port_mq);
536 struct turnstile *send_turnstile = port_send_turnstile(port);
537
538 ip_mq_lock_held(port);
539 assert(port_mq->imq_msgcount > 1 || ipc_kmsg_queue_empty(&port_mq->imq_messages));
540
541 port_mq->imq_msgcount--;
542
543 if (!imq_full(port_mq) && port->ip_fullwaiters &&
544 send_turnstile != TURNSTILE_NULL) {
545 /*
546 * boost the priority of the awoken thread
547 * (WAITQ_PROMOTE_PRIORITY) to ensure it uses
548 * the message queue slot we've just reserved.
549 *
550 * NOTE: this will never prepost
551 *
552 * The wakeup happens on a turnstile waitq
553 * which will wakeup the highest priority waiter.
554 * A potential downside of this would be starving low
555 * priority senders if there is a constant churn of
556 * high priority threads trying to send to this port.
557 */
558 if (waitq_wakeup64_one(&send_turnstile->ts_waitq,
559 IPC_MQUEUE_FULL,
560 THREAD_AWAKENED,
561 WAITQ_PROMOTE_PRIORITY) != KERN_SUCCESS) {
562 port->ip_fullwaiters = false;
563 } else {
564 /* gave away our slot - add reference back */
565 port_mq->imq_msgcount++;
566 }
567 }
568
569 if (ipc_kmsg_queue_empty(&port_mq->imq_messages)) {
570 waitq_clear_prepost_locked(&port->ip_waitq);
571 }
572 }
573
574 /*
575 * Routine: ipc_mqueue_post
576 * Purpose:
577 * Post a message to a waiting receiver or enqueue it. If a
578 * receiver is waiting, we can release our reserved space in
579 * the message queue.
580 *
581 * Conditions:
582 * port is unlocked
583 * If we need to queue, our space in the message queue is reserved.
584 */
585 static void
ipc_mqueue_post(ipc_mqueue_t mqueue,ipc_kmsg_t kmsg,mach_msg_option_t __unused option)586 ipc_mqueue_post(
587 ipc_mqueue_t mqueue,
588 ipc_kmsg_t kmsg,
589 mach_msg_option_t __unused option)
590 {
591 ipc_port_t port = ip_from_mq(mqueue);
592 struct waitq *waitq = &port->ip_waitq;
593 boolean_t destroy_msg = FALSE;
594
595 ipc_kmsg_trace_send(kmsg, option);
596
597 /*
598 * While the msg queue is locked, we have control of the
599 * kmsg, so the ref in it for the port is still good.
600 *
601 * Check for a receiver for the message.
602 */
603 ip_mq_lock(port);
604
605 /* we may have raced with port destruction! */
606 if (!waitq_is_valid(&port->ip_waitq)) {
607 destroy_msg = TRUE;
608 goto out_unlock;
609 }
610
611 for (;;) {
612 spl_t th_spl;
613 thread_t receiver;
614 mach_msg_size_t msize, asize;
615
616 receiver = waitq_wakeup64_identify_locked(waitq,
617 IPC_MQUEUE_RECEIVE, THREAD_AWAKENED,
618 WAITQ_KEEP_LOCKED, &th_spl);
619 /* waitq still locked, thread locked */
620
621 if (receiver == THREAD_NULL) {
622 /*
623 * no receivers; queue kmsg if space still reserved
624 * Reservations are cancelled when the port goes inactive.
625 * note that this will enqueue the message for any
626 * "peeking" receivers.
627 *
628 * Also, post the knote to wake up any threads waiting
629 * on that style of interface if this insertion is of
630 * note (first insertion, or adjusted override qos all
631 * the way to the head of the queue).
632 *
633 * This is just for ports. port-sets knotes are being
634 * posted to by the waitq_wakeup64_identify_locked()
635 * above already.
636 */
637 if (mqueue->imq_msgcount == 0) {
638 /*
639 * The message queue must belong
640 * to an inactive port, so just destroy
641 * the message and pretend it was posted.
642 */
643 destroy_msg = TRUE;
644 } else if (!ipc_kmsg_enqueue_qos(&mqueue->imq_messages, kmsg)) {
645 /*
646 * queue was not empty and qos
647 * didn't change, nothing to do.
648 */
649 } else if (ip_in_a_space(port) &&
650 is_active(ip_get_receiver(port)) &&
651 ipc_port_has_klist(port)) {
652 /*
653 * queue was empty or qos changed
654 * we need to tell kqueue, unless
655 * the space is getting torn down
656 */
657 KNOTE(&port->ip_klist, 0);
658 }
659 break;
660 }
661
662 /*
663 * If a thread is attempting a "peek" into the message queue
664 * (MACH_PEEK_IN_PROGRESS), then we enqueue the message and set the
665 * thread running. A successful peek is essentially the same as
666 * message delivery since the peeking thread takes responsibility
667 * for delivering the message and (eventually) removing it from
668 * the mqueue. Only one thread can successfully use the peek
669 * facility on any given port, so we exit the waitq loop after
670 * encountering such a thread.
671 */
672 if (receiver->ith_state == MACH_PEEK_IN_PROGRESS && mqueue->imq_msgcount > 0) {
673 ipc_kmsg_enqueue_qos(&mqueue->imq_messages, kmsg);
674 ipc_mqueue_peek_on_thread_locked(mqueue, receiver->ith_option, receiver);
675 thread_unlock(receiver);
676 splx(th_spl);
677 break; /* Message was posted, so break out of loop */
678 }
679
680 /*
681 * If the receiver waited with a facility not directly related
682 * to Mach messaging, then it isn't prepared to get handed the
683 * message directly. Just set it running, and go look for
684 * another thread that can.
685 */
686 if (receiver->ith_state != MACH_RCV_IN_PROGRESS) {
687 thread_unlock(receiver);
688 splx(th_spl);
689 continue;
690 }
691
692
693 /*
694 * We found a waiting thread.
695 * If the message is too large or the scatter list is too small
696 * the thread we wake up will get that as its status.
697 */
698 msize = ipc_kmsg_copyout_size(kmsg, receiver->map);
699 asize = ipc_kmsg_aux_data_size(kmsg);
700
701 if (ipc_kmsg_too_large(msize, asize, receiver->ith_option,
702 receiver->ith_max_msize, receiver->ith_max_asize, receiver)) {
703 receiver->ith_msize = msize;
704 receiver->ith_asize = asize;
705 receiver->ith_state = MACH_RCV_TOO_LARGE;
706 } else {
707 receiver->ith_state = MACH_MSG_SUCCESS;
708 }
709
710 /*
711 * If there is no problem with the upcoming receive, or the
712 * receiver thread didn't specifically ask for special too
713 * large error condition, go ahead and select it anyway.
714 */
715 if ((receiver->ith_state == MACH_MSG_SUCCESS) ||
716 !(receiver->ith_option & MACH_RCV_LARGE)) {
717 receiver->ith_kmsg = kmsg;
718 receiver->ith_seqno = mqueue->imq_seqno++;
719 #if MACH_FLIPC
720 mach_node_t node = kmsg->ikm_node;
721 #endif
722 thread_unlock(receiver);
723 splx(th_spl);
724
725 /* we didn't need our reserved spot in the queue */
726 ipc_mqueue_release_msgcount(mqueue);
727
728 #if MACH_FLIPC
729 if (MACH_NODE_VALID(node) && FPORT_VALID(mqueue->imq_fport)) {
730 flipc_msg_ack(node, mqueue, TRUE);
731 }
732 #endif
733 break;
734 }
735
736 /*
737 * Otherwise, this thread needs to be released to run
738 * and handle its error without getting the message. We
739 * need to go back and pick another one.
740 */
741 receiver->ith_receiver_name = mqueue->imq_receiver_name;
742 receiver->ith_kmsg = IKM_NULL;
743 receiver->ith_seqno = 0;
744 thread_unlock(receiver);
745 splx(th_spl);
746 }
747
748 out_unlock:
749 /* clear the waitq boost we may have been given */
750 waitq_clear_promotion_locked(waitq, current_thread());
751 waitq_unlock(waitq);
752
753 if (destroy_msg) {
754 ipc_kmsg_destroy(kmsg, IPC_KMSG_DESTROY_ALL);
755 }
756
757 counter_inc(¤t_task()->messages_sent);
758 return;
759 }
760
761
762 static void
ipc_mqueue_receive_results(wait_result_t saved_wait_result)763 ipc_mqueue_receive_results(wait_result_t saved_wait_result)
764 {
765 thread_t self = current_thread();
766 mach_msg_option64_t option64 = self->ith_option;
767
768 /*
769 * why did we wake up?
770 */
771 switch (saved_wait_result) {
772 case THREAD_TIMED_OUT:
773 self->ith_state = MACH_RCV_TIMED_OUT;
774 return;
775
776 case THREAD_INTERRUPTED:
777 self->ith_state = MACH_RCV_INTERRUPTED;
778 return;
779
780 case THREAD_RESTART:
781 /* something bad happened to the port/set */
782 self->ith_state = MACH_RCV_PORT_CHANGED;
783 return;
784
785 case THREAD_AWAKENED:
786 /*
787 * We do not need to go select a message, somebody
788 * handed us one (or a too-large indication).
789 */
790 switch (self->ith_state) {
791 case MACH_RCV_SCATTER_SMALL:
792 case MACH_RCV_TOO_LARGE:
793 /*
794 * Somebody tried to give us a too large
795 * message. If we indicated that we cared,
796 * then they only gave us the indication,
797 * otherwise they gave us the indication
798 * AND the message anyway.
799 */
800 if (option64 & MACH_RCV_LARGE) {
801 return;
802 }
803 return;
804 case MACH_MSG_SUCCESS:
805 return;
806 case MACH_PEEK_READY:
807 return;
808
809 default:
810 panic("ipc_mqueue_receive_results: strange ith_state");
811 }
812
813 default:
814 panic("ipc_mqueue_receive_results: strange wait_result");
815 }
816 }
817
818 void
ipc_mqueue_receive_continue(__unused void * param,wait_result_t wresult)819 ipc_mqueue_receive_continue(
820 __unused void *param,
821 wait_result_t wresult)
822 {
823 ipc_mqueue_receive_results(wresult);
824 mach_msg_receive_continue(); /* hard-coded for now */
825 }
826
827 /*
828 * Routine: ipc_mqueue_receive
829 * Purpose:
830 * Receive a message from a message queue.
831 *
832 * Conditions:
833 * Our caller must hold a reference for the port or port set
834 * to which this queue belongs, to keep the queue
835 * from being deallocated.
836 *
837 * The kmsg is returned with clean header fields
838 * and with the circular bit turned off through the ith_kmsg
839 * field of the thread's receive continuation state.
840 * Returns:
841 * MACH_MSG_SUCCESS Message returned in ith_kmsg.
842 * MACH_RCV_TOO_LARGE Message size returned in ith_msize,
843 * Auxiliary data size returned in ith_asize
844 * MACH_RCV_TIMED_OUT No message obtained.
845 * MACH_RCV_INTERRUPTED No message obtained.
846 * MACH_RCV_PORT_DIED Port/set died; no message.
847 * MACH_RCV_PORT_CHANGED Port moved into set; no msg.
848 *
849 */
850
851 void
ipc_mqueue_receive(struct waitq * waitq,mach_msg_option64_t option64,mach_msg_size_t max_size,mach_msg_size_t max_aux_size,mach_msg_timeout_t rcv_timeout,int interruptible,bool has_continuation)852 ipc_mqueue_receive(
853 struct waitq *waitq,
854 mach_msg_option64_t option64,
855 mach_msg_size_t max_size,
856 mach_msg_size_t max_aux_size, /* 0 if no aux buffer */
857 mach_msg_timeout_t rcv_timeout,
858 int interruptible,
859 bool has_continuation)
860 {
861 wait_result_t wresult;
862 thread_t self = current_thread();
863
864 waitq_lock(waitq);
865
866 wresult = ipc_mqueue_receive_on_thread_and_unlock(waitq, option64, max_size,
867 max_aux_size, rcv_timeout, interruptible, self);
868 /* object unlocked */
869 if (wresult == THREAD_NOT_WAITING) {
870 return;
871 }
872
873 if (wresult == THREAD_WAITING) {
874 if (has_continuation) {
875 wresult = thread_block(ipc_mqueue_receive_continue);
876 /* NOTREACHED */
877 }
878 wresult = thread_block(THREAD_CONTINUE_NULL);
879 }
880 ipc_mqueue_receive_results(wresult);
881 }
882
883 /*
884 * Routine: ipc_mqueue_receive_on_thread_and_unlock
885 * Purpose:
886 * Receive a message from a message queue using a specified thread.
887 * If no message available, assert_wait on the appropriate waitq.
888 *
889 * Conditions:
890 * Assumes thread is self.
891 * The port/port-set waitq is locked on entry, unlocked on return.
892 * May have assert-waited. Caller must block in those cases.
893 */
894 wait_result_t
ipc_mqueue_receive_on_thread_and_unlock(struct waitq * waitq,mach_msg_option64_t option64,mach_msg_size_t max_msg_size,mach_msg_size_t max_aux_size,mach_msg_timeout_t rcv_timeout,int interruptible,thread_t thread)895 ipc_mqueue_receive_on_thread_and_unlock(
896 struct waitq *waitq,
897 mach_msg_option64_t option64,
898 mach_msg_size_t max_msg_size,
899 mach_msg_size_t max_aux_size,
900 mach_msg_timeout_t rcv_timeout,
901 int interruptible,
902 thread_t thread)
903 {
904 ipc_object_t object = io_from_waitq(waitq);
905 ipc_port_t port = IP_NULL;
906 wait_result_t wresult;
907 uint64_t deadline;
908 struct turnstile *rcv_turnstile = TURNSTILE_NULL;
909
910 if (waitq_type(waitq) == WQT_PORT_SET) {
911 ipc_pset_t pset = ips_object_to_pset(object);
912 struct waitq *port_wq;
913
914 /*
915 * Put the message at the back of the prepost list
916 * if it's not a PEEK.
917 *
918 * Might drop the pset lock temporarily.
919 */
920 port_wq = waitq_set_first_prepost(&pset->ips_wqset, WQS_PREPOST_LOCK |
921 ((option64 & MACH64_PEEK_MSG) ? WQS_PREPOST_PEEK: 0));
922
923 /* Returns with port locked */
924
925 if (port_wq != NULL) {
926 /*
927 * We get here if there is at least one message
928 * waiting on port_wq. We have instructed the prepost
929 * iteration logic to leave both the port_wq and the
930 * set waitq locked.
931 *
932 * Continue on to handling the message with just
933 * the port waitq locked.
934 */
935 io_unlock(object);
936 port = ip_from_waitq(port_wq);
937 }
938 } else if (waitq_type(waitq) == WQT_PORT) {
939 port = ip_from_waitq(waitq);
940 if (ipc_kmsg_queue_empty(&port->ip_messages.imq_messages)) {
941 port = IP_NULL;
942 }
943 } else {
944 panic("Unknown waitq type (%p/0x%x)", waitq, waitq_type(waitq));
945 }
946
947 if (port) {
948 if (option64 & MACH64_PEEK_MSG) {
949 ipc_mqueue_peek_on_thread_locked(&port->ip_messages,
950 option64, thread);
951 } else {
952 ipc_mqueue_select_on_thread_locked(&port->ip_messages,
953 option64, max_msg_size, max_aux_size, thread);
954 }
955 ip_mq_unlock(port);
956 return THREAD_NOT_WAITING;
957 }
958
959 if (!waitq_is_valid(waitq)) {
960 /* someone raced us to destroy this mqueue/port! */
961 io_unlock(object);
962 /*
963 * ipc_mqueue_receive_results updates the thread's ith_state
964 * TODO: differentiate between rights being moved and
965 * rights/ports being destroyed (21885327)
966 */
967 return THREAD_RESTART;
968 }
969
970 /*
971 * Looks like we'll have to block. The waitq we will
972 * block on (whether the set's or the local port's) is
973 * still locked.
974 */
975 if ((option64 & MACH_RCV_TIMEOUT) && rcv_timeout == 0) {
976 io_unlock(object);
977 thread->ith_state = MACH_RCV_TIMED_OUT;
978 return THREAD_NOT_WAITING;
979 }
980
981 thread->ith_option = option64;
982 thread->ith_max_msize = max_msg_size;
983 thread->ith_msize = 0;
984
985 thread->ith_max_asize = max_aux_size;
986 thread->ith_asize = 0;
987
988 if (option64 & MACH64_PEEK_MSG) {
989 thread->ith_state = MACH_PEEK_IN_PROGRESS;
990 } else {
991 thread->ith_state = MACH_RCV_IN_PROGRESS;
992 }
993
994 if (option64 & MACH_RCV_TIMEOUT) {
995 clock_interval_to_deadline(rcv_timeout, 1000 * NSEC_PER_USEC, &deadline);
996 } else {
997 deadline = 0;
998 }
999
1000 /*
1001 * Threads waiting on a reply port (not portset)
1002 * will wait on its receive turnstile.
1003 *
1004 * Donate waiting thread's turnstile and
1005 * setup inheritor for special reply port.
1006 * Based on the state of the special reply
1007 * port, the inheritor would be the send
1008 * turnstile of the connection port on which
1009 * the send of sync ipc would happen or
1010 * workloop's turnstile who would reply to
1011 * the sync ipc message.
1012 *
1013 * Pass in mqueue wait in waitq_assert_wait to
1014 * support port set wakeup. The mqueue waitq of port
1015 * will be converted to to turnstile waitq
1016 * in waitq_assert_wait instead of global waitqs.
1017 */
1018 if (waitq_type(waitq) == WQT_PORT) {
1019 port = ip_from_waitq(waitq);
1020 rcv_turnstile = turnstile_prepare((uintptr_t)port,
1021 port_rcv_turnstile_address(port),
1022 TURNSTILE_NULL, TURNSTILE_SYNC_IPC);
1023
1024 ipc_port_recv_update_inheritor(port, rcv_turnstile,
1025 TURNSTILE_DELAYED_UPDATE);
1026 }
1027
1028 thread_set_pending_block_hint(thread, kThreadWaitPortReceive);
1029 wresult = waitq_assert_wait64_locked(waitq,
1030 IPC_MQUEUE_RECEIVE,
1031 interruptible,
1032 TIMEOUT_URGENCY_USER_NORMAL,
1033 deadline,
1034 TIMEOUT_NO_LEEWAY,
1035 thread);
1036 if (wresult == THREAD_AWAKENED) {
1037 /*
1038 * The first thing we did was to look for preposts
1039 * (using waitq_set_first_prepost() for sets, or looking
1040 * at the port's queue for ports).
1041 *
1042 * Since we found none, we kept the waitq locked.
1043 *
1044 * It ensures that waitq_assert_wait64_locked() can't
1045 * find pre-posts either, won't drop the waitq lock
1046 * either (even for a set), and can't return THREAD_AWAKENED.
1047 */
1048 panic("ipc_mqueue_receive_on_thread: sleep walking");
1049 }
1050
1051 io_unlock(object);
1052
1053 /* Check if its a port mqueue and if it needs to call turnstile_update_inheritor_complete */
1054 if (rcv_turnstile != TURNSTILE_NULL) {
1055 turnstile_update_inheritor_complete(rcv_turnstile, TURNSTILE_INTERLOCK_NOT_HELD);
1056 }
1057 /* Its callers responsibility to call turnstile_complete to get the turnstile back */
1058
1059 return wresult;
1060 }
1061
1062
1063 /*
1064 * Routine: ipc_mqueue_peek_on_thread_locked
1065 * Purpose:
1066 * A receiver discovered that there was a message on the queue
1067 * before he had to block. Tell a thread about the message queue,
1068 * but don't pick off any messages.
1069 * Conditions:
1070 * port_mq locked
1071 * at least one message on port_mq's message queue
1072 *
1073 * Returns: (on thread->ith_state)
1074 * MACH_PEEK_READY ith_peekq contains a message queue
1075 */
1076 void
ipc_mqueue_peek_on_thread_locked(ipc_mqueue_t port_mq,__assert_only mach_msg_option64_t option64,thread_t thread)1077 ipc_mqueue_peek_on_thread_locked(
1078 ipc_mqueue_t port_mq,
1079 __assert_only mach_msg_option64_t option64,
1080 thread_t thread)
1081 {
1082 assert(option64 & MACH64_PEEK_MSG);
1083 assert(ipc_kmsg_queue_first(&port_mq->imq_messages) != IKM_NULL);
1084
1085 /*
1086 * Take a reference on the mqueue's associated port:
1087 * the peeking thread will be responsible to release this reference
1088 */
1089 ip_validate(ip_from_mq(port_mq));
1090 ip_reference(ip_from_mq(port_mq));
1091 thread->ith_peekq = port_mq;
1092 thread->ith_state = MACH_PEEK_READY;
1093 }
1094
1095 /*
1096 * Routine: ipc_mqueue_select_on_thread_locked
1097 * Purpose:
1098 * A receiver discovered that there was a message on the queue
1099 * before he had to block. Pick the message off the queue and
1100 * "post" it to thread.
1101 * Conditions:
1102 * port locked.
1103 * thread not locked.
1104 * There is a message.
1105 * No need to reserve prepost objects - it will never prepost
1106 *
1107 * Returns:
1108 * MACH_MSG_SUCCESS Actually selected a message for ourselves.
1109 * MACH_RCV_TOO_LARGE May or may not have pull it, but it is large
1110 */
1111 void
ipc_mqueue_select_on_thread_locked(ipc_mqueue_t port_mq,mach_msg_option64_t option64,mach_msg_size_t max_msg_size,mach_msg_size_t max_aux_size,thread_t thread)1112 ipc_mqueue_select_on_thread_locked(
1113 ipc_mqueue_t port_mq,
1114 mach_msg_option64_t option64,
1115 mach_msg_size_t max_msg_size,
1116 mach_msg_size_t max_aux_size,
1117 thread_t thread)
1118 {
1119 ipc_kmsg_t kmsg;
1120 mach_msg_size_t msize, asize;
1121
1122 mach_msg_return_t mr = MACH_MSG_SUCCESS;
1123
1124 /*
1125 * Do some sanity checking of our ability to receive
1126 * before pulling the message off the queue.
1127 */
1128 kmsg = ipc_kmsg_queue_first(&port_mq->imq_messages);
1129 assert(kmsg != IKM_NULL);
1130
1131 /*
1132 * If we really can't receive it, but we had the
1133 * MACH_RCV_LARGE option set, then don't take it off
1134 * the queue, instead return the appropriate error
1135 * (and size needed).
1136 */
1137 msize = ipc_kmsg_copyout_size(kmsg, thread->map);
1138 asize = ipc_kmsg_aux_data_size(kmsg);
1139
1140 if (ipc_kmsg_too_large(msize, asize, option64,
1141 max_msg_size, max_aux_size, thread)) {
1142 mr = MACH_RCV_TOO_LARGE;
1143 if (option64 & MACH_RCV_LARGE) {
1144 thread->ith_receiver_name = port_mq->imq_receiver_name;
1145 thread->ith_kmsg = IKM_NULL;
1146 thread->ith_msize = msize;
1147 thread->ith_asize = asize;
1148 thread->ith_seqno = 0;
1149 thread->ith_state = mr;
1150 return;
1151 }
1152 }
1153
1154 ipc_kmsg_rmqueue(&port_mq->imq_messages, kmsg);
1155 #if MACH_FLIPC
1156 if (MACH_NODE_VALID(kmsg->ikm_node) && FPORT_VALID(port_mq->imq_fport)) {
1157 flipc_msg_ack(kmsg->ikm_node, port_mq, TRUE);
1158 }
1159 #endif
1160 ipc_mqueue_release_msgcount(port_mq);
1161 thread->ith_seqno = port_mq->imq_seqno++;
1162 thread->ith_kmsg = kmsg;
1163 thread->ith_state = mr;
1164
1165 counter_inc(¤t_task()->messages_received);
1166 return;
1167 }
1168
1169 /*
1170 * Routine: ipc_mqueue_peek_locked
1171 * Purpose:
1172 * Peek at a (non-set) message queue to see if it has a message
1173 * matching the sequence number provided (if zero, then the
1174 * first message in the queue) and return vital info about the
1175 * message.
1176 *
1177 * Conditions:
1178 * The io object corresponding to mq is locked by callers.
1179 * Other locks may be held by callers, so this routine cannot block.
1180 * Caller holds reference on the message queue.
1181 */
1182 unsigned
ipc_mqueue_peek_locked(ipc_mqueue_t mq,mach_port_seqno_t * seqnop,mach_msg_size_t * msg_sizep,mach_msg_id_t * msg_idp,mach_msg_max_trailer_t * msg_trailerp,ipc_kmsg_t * kmsgp)1183 ipc_mqueue_peek_locked(ipc_mqueue_t mq,
1184 mach_port_seqno_t * seqnop,
1185 mach_msg_size_t * msg_sizep,
1186 mach_msg_id_t * msg_idp,
1187 mach_msg_max_trailer_t * msg_trailerp,
1188 ipc_kmsg_t *kmsgp)
1189 {
1190 ipc_kmsg_queue_t kmsgq;
1191 ipc_kmsg_t kmsg;
1192 mach_port_seqno_t seqno, msgoff;
1193 unsigned res = 0;
1194 mach_msg_header_t *hdr;
1195
1196 seqno = 0;
1197 if (seqnop != NULL) {
1198 seqno = *seqnop;
1199 }
1200
1201 if (seqno == 0) {
1202 seqno = mq->imq_seqno;
1203 msgoff = 0;
1204 } else if (seqno >= mq->imq_seqno &&
1205 seqno < mq->imq_seqno + mq->imq_msgcount) {
1206 msgoff = seqno - mq->imq_seqno;
1207 } else {
1208 goto out;
1209 }
1210
1211 /* look for the message that would match that seqno */
1212 kmsgq = &mq->imq_messages;
1213 kmsg = ipc_kmsg_queue_first(kmsgq);
1214 while (msgoff-- && kmsg != IKM_NULL) {
1215 kmsg = ipc_kmsg_queue_next(kmsgq, kmsg);
1216 }
1217 if (kmsg == IKM_NULL) {
1218 goto out;
1219 }
1220
1221 #if __has_feature(ptrauth_calls)
1222 /*
1223 * Validate kmsg signature before doing anything with it. Since we are holding
1224 * the mqueue lock here, and only header + trailer will be peeked on, just
1225 * do a partial validation to finish quickly.
1226 *
1227 * Partial kmsg signature is only supported on PAC devices.
1228 */
1229 ipc_kmsg_validate_sig(kmsg, true);
1230 #endif
1231
1232 hdr = ikm_header(kmsg);
1233 /* found one - return the requested info */
1234 if (seqnop != NULL) {
1235 *seqnop = seqno;
1236 }
1237 if (msg_sizep != NULL) {
1238 *msg_sizep = hdr->msgh_size;
1239 }
1240 if (msg_idp != NULL) {
1241 *msg_idp = hdr->msgh_id;
1242 }
1243 if (msg_trailerp != NULL) {
1244 memcpy(msg_trailerp, ipc_kmsg_get_trailer(kmsg, false), sizeof(mach_msg_max_trailer_t));
1245 }
1246 if (kmsgp != NULL) {
1247 *kmsgp = kmsg;
1248 }
1249
1250 res = 1;
1251
1252 out:
1253 return res;
1254 }
1255
1256
1257 /*
1258 * Routine: ipc_mqueue_peek
1259 * Purpose:
1260 * Peek at a (non-set) message queue to see if it has a message
1261 * matching the sequence number provided (if zero, then the
1262 * first message in the queue) and return vital info about the
1263 * message.
1264 *
1265 * Conditions:
1266 * The ipc_mqueue_t is unlocked.
1267 * Locks may be held by callers, so this routine cannot block.
1268 * Caller holds reference on the message queue.
1269 */
1270 unsigned
ipc_mqueue_peek(ipc_mqueue_t mq,mach_port_seqno_t * seqnop,mach_msg_size_t * msg_sizep,mach_msg_id_t * msg_idp,mach_msg_max_trailer_t * msg_trailerp,ipc_kmsg_t * kmsgp)1271 ipc_mqueue_peek(ipc_mqueue_t mq,
1272 mach_port_seqno_t * seqnop,
1273 mach_msg_size_t * msg_sizep,
1274 mach_msg_id_t * msg_idp,
1275 mach_msg_max_trailer_t * msg_trailerp,
1276 ipc_kmsg_t *kmsgp)
1277 {
1278 ipc_port_t port = ip_from_mq(mq);
1279 unsigned res;
1280
1281 ip_mq_lock(port);
1282
1283 res = ipc_mqueue_peek_locked(mq, seqnop, msg_sizep, msg_idp,
1284 msg_trailerp, kmsgp);
1285
1286 ip_mq_unlock(port);
1287 return res;
1288 }
1289
1290 #if MACH_FLIPC
1291 /*
1292 * Routine: ipc_mqueue_release_peek_ref
1293 * Purpose:
1294 * Release the reference on an mqueue's associated port which was
1295 * granted to a thread in ipc_mqueue_peek_on_thread (on the
1296 * MACH64_PEEK_MSG thread wakeup path).
1297 *
1298 * Conditions:
1299 * The ipc_mqueue_t should be locked on entry.
1300 * The ipc_mqueue_t will be _unlocked_ on return
1301 * (and potentially invalid!)
1302 *
1303 */
1304 void
ipc_mqueue_release_peek_ref(ipc_mqueue_t mqueue)1305 ipc_mqueue_release_peek_ref(ipc_mqueue_t mqueue)
1306 {
1307 ipc_port_t port = ip_from_mq(mqueue);
1308
1309 ip_mq_lock_held(port);
1310
1311 /*
1312 * clear any preposts this mq may have generated
1313 * (which would cause subsequent immediate wakeups)
1314 */
1315 waitq_clear_prepost_locked(&port->ip_waitq);
1316
1317 ip_mq_unlock(port);
1318
1319 /*
1320 * release the port reference: we need to do this outside the lock
1321 * because we might be holding the last port reference!
1322 **/
1323 ip_release(port);
1324 }
1325 #endif /* MACH_FLIPC */
1326
1327 /*
1328 * Routine: ipc_mqueue_destroy_locked
1329 * Purpose:
1330 * Destroy a message queue.
1331 * Set any blocked senders running.
1332 * Destroy the kmsgs in the queue.
1333 * Conditions:
1334 * port locked
1335 * Receivers were removed when the receive right was "changed"
1336 */
1337 boolean_t
ipc_mqueue_destroy_locked(ipc_mqueue_t mqueue,waitq_link_list_t * free_l)1338 ipc_mqueue_destroy_locked(ipc_mqueue_t mqueue, waitq_link_list_t *free_l)
1339 {
1340 ipc_port_t port = ip_from_mq(mqueue);
1341 boolean_t reap = FALSE;
1342 struct turnstile *send_turnstile = port_send_turnstile(port);
1343
1344 /*
1345 * rouse all blocked senders
1346 * (don't boost anyone - we're tearing this queue down)
1347 * (never preposts)
1348 */
1349 port->ip_fullwaiters = false;
1350
1351 if (send_turnstile != TURNSTILE_NULL) {
1352 waitq_wakeup64_all(&send_turnstile->ts_waitq,
1353 IPC_MQUEUE_FULL,
1354 THREAD_RESTART, WAITQ_WAKEUP_DEFAULT);
1355 }
1356
1357 #if MACH_FLIPC
1358 ipc_kmsg_t kmsg;
1359
1360 cqe_foreach_element_safe(kmsg, &mqueue->imq_messages, ikm_link) {
1361 if (MACH_NODE_VALID(kmsg->ikm_node) &&
1362 FPORT_VALID(mqueue->imq_fport)) {
1363 flipc_msg_ack(kmsg->ikm_node, mqueue, TRUE);
1364 }
1365 }
1366 #endif
1367
1368 /*
1369 * Move messages from the specified queue to the per-thread
1370 * clean/drain queue while we have the mqueue lock.
1371 */
1372 reap = ipc_kmsg_delayed_destroy_queue(&mqueue->imq_messages);
1373
1374 /*
1375 * Wipe out message count, both for messages about to be
1376 * reaped and for reserved space for (previously) woken senders.
1377 * This is the indication to them that their reserved space is gone
1378 * (the mqueue was destroyed).
1379 */
1380 mqueue->imq_msgcount = 0;
1381
1382 /*
1383 * invalidate the waitq for subsequent mqueue operations,
1384 * the port lock could be dropped after invalidating the mqueue.
1385 */
1386
1387 waitq_invalidate(&port->ip_waitq);
1388
1389 waitq_unlink_all_locked(&port->ip_waitq, NULL, free_l);
1390
1391 return reap;
1392 }
1393
1394 /*
1395 * Routine: ipc_mqueue_set_qlimit_locked
1396 * Purpose:
1397 * Changes a message queue limit; the maximum number
1398 * of messages which may be queued.
1399 * Conditions:
1400 * Port locked.
1401 */
1402
1403 void
ipc_mqueue_set_qlimit_locked(ipc_mqueue_t mqueue,mach_port_msgcount_t qlimit)1404 ipc_mqueue_set_qlimit_locked(
1405 ipc_mqueue_t mqueue,
1406 mach_port_msgcount_t qlimit)
1407 {
1408 ipc_port_t port = ip_from_mq(mqueue);
1409
1410 assert(qlimit <= MACH_PORT_QLIMIT_MAX);
1411
1412 /* wake up senders allowed by the new qlimit */
1413 if (qlimit > mqueue->imq_qlimit) {
1414 mach_port_msgcount_t i, wakeup;
1415 struct turnstile *send_turnstile = port_send_turnstile(port);
1416
1417 /* caution: wakeup, qlimit are unsigned */
1418 wakeup = qlimit - mqueue->imq_qlimit;
1419
1420 for (i = 0; i < wakeup; i++) {
1421 /*
1422 * boost the priority of the awoken thread
1423 * (WAITQ_PROMOTE_PRIORITY) to ensure it uses
1424 * the message queue slot we've just reserved.
1425 *
1426 * NOTE: this will never prepost
1427 */
1428 if (send_turnstile == TURNSTILE_NULL ||
1429 waitq_wakeup64_one(&send_turnstile->ts_waitq,
1430 IPC_MQUEUE_FULL,
1431 THREAD_AWAKENED,
1432 WAITQ_PROMOTE_PRIORITY) == KERN_NOT_WAITING) {
1433 port->ip_fullwaiters = false;
1434 break;
1435 }
1436 mqueue->imq_msgcount++; /* give it to the awakened thread */
1437 }
1438 }
1439 mqueue->imq_qlimit = (uint16_t)qlimit;
1440 }
1441
1442 /*
1443 * Routine: ipc_mqueue_set_seqno_locked
1444 * Purpose:
1445 * Changes an mqueue's sequence number.
1446 * Conditions:
1447 * Caller holds a reference to the queue's containing object.
1448 */
1449 void
ipc_mqueue_set_seqno_locked(ipc_mqueue_t mqueue,mach_port_seqno_t seqno)1450 ipc_mqueue_set_seqno_locked(
1451 ipc_mqueue_t mqueue,
1452 mach_port_seqno_t seqno)
1453 {
1454 mqueue->imq_seqno = seqno;
1455 }
1456
1457
1458 /*
1459 * Routine: ipc_mqueue_copyin
1460 * Purpose:
1461 * Convert a name in a space to a message queue.
1462 * Conditions:
1463 * Nothing locked. If successful, the caller gets a ref for
1464 * for the object. This ref ensures the continued existence of
1465 * the queue.
1466 * Returns:
1467 * MACH_MSG_SUCCESS Found a message queue.
1468 * MACH_RCV_INVALID_NAME The space is dead.
1469 * MACH_RCV_INVALID_NAME The name doesn't denote a right.
1470 * MACH_RCV_INVALID_NAME
1471 * The denoted right is not receive or port set.
1472 * MACH_RCV_IN_SET Receive right is a member of a set.
1473 */
1474
1475 mach_msg_return_t
ipc_mqueue_copyin(ipc_space_t space,mach_port_name_t name,ipc_object_t * objectp)1476 ipc_mqueue_copyin(
1477 ipc_space_t space,
1478 mach_port_name_t name,
1479 ipc_object_t *objectp)
1480 {
1481 ipc_entry_bits_t bits;
1482 ipc_object_t object;
1483 kern_return_t kr;
1484
1485 kr = ipc_right_lookup_read(space, name, &bits, &object);
1486 if (kr != KERN_SUCCESS) {
1487 return MACH_RCV_INVALID_NAME;
1488 }
1489 /* object is locked and active */
1490
1491 if (bits & MACH_PORT_TYPE_RECEIVE) {
1492 __assert_only ipc_port_t port = ip_object_to_port(object);
1493 assert(ip_get_receiver_name(port) == name);
1494 assert(ip_in_space(port, space));
1495 }
1496 if (bits & (MACH_PORT_TYPE_RECEIVE | MACH_PORT_TYPE_PORT_SET)) {
1497 io_reference(object);
1498 io_unlock(object);
1499 } else {
1500 io_unlock(object);
1501 /* guard exception if we never held the receive right in this entry */
1502 if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
1503 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_RCV_INVALID_NAME);
1504 }
1505 return MACH_RCV_INVALID_NAME;
1506 }
1507
1508 *objectp = object;
1509 return MACH_MSG_SUCCESS;
1510 }
1511