]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ipc/ipc_port.c
xnu-3247.10.11.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_port.c
1 /*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @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 Software.Distribution@CS.CMU.EDU
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 * NOTICE: This file was modified by McAfee Research in 2004 to introduce
58 * support for mandatory and extensible security protections. This notice
59 * is included in support of clause 2.2 (b) of the Apple Public License,
60 * Version 2.0.
61 */
62 /*
63 */
64 /*
65 * File: ipc/ipc_port.c
66 * Author: Rich Draves
67 * Date: 1989
68 *
69 * Functions to manipulate IPC ports.
70 */
71
72 #include <zone_debug.h>
73 #include <mach_assert.h>
74
75 #include <mach/port.h>
76 #include <mach/kern_return.h>
77 #include <kern/ipc_kobject.h>
78 #include <kern/thread.h>
79 #include <kern/misc_protos.h>
80 #include <kern/waitq.h>
81 #include <ipc/ipc_entry.h>
82 #include <ipc/ipc_space.h>
83 #include <ipc/ipc_object.h>
84 #include <ipc/ipc_port.h>
85 #include <ipc/ipc_pset.h>
86 #include <ipc/ipc_kmsg.h>
87 #include <ipc/ipc_mqueue.h>
88 #include <ipc/ipc_notify.h>
89 #include <ipc/ipc_table.h>
90 #include <ipc/ipc_importance.h>
91
92 #include <security/mac_mach_internal.h>
93
94 #include <string.h>
95
96 decl_lck_spin_data(, ipc_port_multiple_lock_data)
97 ipc_port_timestamp_t ipc_port_timestamp_data;
98 int ipc_portbt;
99
100 #if MACH_ASSERT
101 void ipc_port_init_debug(
102 ipc_port_t port,
103 uintptr_t *callstack,
104 unsigned int callstack_max);
105
106 void ipc_port_callstack_init_debug(
107 uintptr_t *callstack,
108 unsigned int callstack_max);
109
110 #endif /* MACH_ASSERT */
111
112 void
113 ipc_port_release(ipc_port_t port)
114 {
115 ip_release(port);
116 }
117
118 void
119 ipc_port_reference(ipc_port_t port)
120 {
121 ip_reference(port);
122 }
123
124 /*
125 * Routine: ipc_port_timestamp
126 * Purpose:
127 * Retrieve a timestamp value.
128 */
129
130 ipc_port_timestamp_t
131 ipc_port_timestamp(void)
132 {
133 return OSIncrementAtomic(&ipc_port_timestamp_data);
134 }
135
136 /*
137 * Routine: ipc_port_request_alloc
138 * Purpose:
139 * Try to allocate a request slot.
140 * If successful, returns the request index.
141 * Otherwise returns zero.
142 * Conditions:
143 * The port is locked and active.
144 * Returns:
145 * KERN_SUCCESS A request index was found.
146 * KERN_NO_SPACE No index allocated.
147 */
148
149 #if IMPORTANCE_INHERITANCE
150 kern_return_t
151 ipc_port_request_alloc(
152 ipc_port_t port,
153 mach_port_name_t name,
154 ipc_port_t soright,
155 boolean_t send_possible,
156 boolean_t immediate,
157 ipc_port_request_index_t *indexp,
158 boolean_t *importantp)
159 #else
160 kern_return_t
161 ipc_port_request_alloc(
162 ipc_port_t port,
163 mach_port_name_t name,
164 ipc_port_t soright,
165 boolean_t send_possible,
166 boolean_t immediate,
167 ipc_port_request_index_t *indexp)
168 #endif /* IMPORTANCE_INHERITANCE */
169 {
170 ipc_port_request_t ipr, table;
171 ipc_port_request_index_t index;
172 uintptr_t mask = 0;
173
174 #if IMPORTANCE_INHERITANCE
175 *importantp = FALSE;
176 #endif /* IMPORTANCE_INHERITANCE */
177
178 assert(ip_active(port));
179 assert(name != MACH_PORT_NULL);
180 assert(soright != IP_NULL);
181
182 table = port->ip_requests;
183
184 if (table == IPR_NULL)
185 return KERN_NO_SPACE;
186
187 index = table->ipr_next;
188 if (index == 0)
189 return KERN_NO_SPACE;
190
191 ipr = &table[index];
192 assert(ipr->ipr_name == MACH_PORT_NULL);
193
194 table->ipr_next = ipr->ipr_next;
195 ipr->ipr_name = name;
196
197 if (send_possible) {
198 mask |= IPR_SOR_SPREQ_MASK;
199 if (immediate) {
200 mask |= IPR_SOR_SPARM_MASK;
201 if (port->ip_sprequests == 0) {
202 port->ip_sprequests = 1;
203 #if IMPORTANCE_INHERITANCE
204 /* TODO: Live importance support in send-possible */
205 if (port->ip_impdonation != 0 &&
206 port->ip_spimportant == 0 &&
207 (task_is_importance_donor(current_task()))) {
208 port->ip_spimportant = 1;
209 *importantp = TRUE;
210 }
211 #endif /* IMPORTANCE_INHERTANCE */
212 }
213 }
214 }
215 ipr->ipr_soright = IPR_SOR_MAKE(soright, mask);
216
217 *indexp = index;
218
219 return KERN_SUCCESS;
220 }
221
222 /*
223 * Routine: ipc_port_request_grow
224 * Purpose:
225 * Grow a port's table of requests.
226 * Conditions:
227 * The port must be locked and active.
228 * Nothing else locked; will allocate memory.
229 * Upon return the port is unlocked.
230 * Returns:
231 * KERN_SUCCESS Grew the table.
232 * KERN_SUCCESS Somebody else grew the table.
233 * KERN_SUCCESS The port died.
234 * KERN_RESOURCE_SHORTAGE Couldn't allocate new table.
235 * KERN_NO_SPACE Couldn't grow to desired size
236 */
237
238 kern_return_t
239 ipc_port_request_grow(
240 ipc_port_t port,
241 ipc_table_elems_t target_size)
242 {
243 ipc_table_size_t its;
244 ipc_port_request_t otable, ntable;
245
246 assert(ip_active(port));
247
248 otable = port->ip_requests;
249 if (otable == IPR_NULL)
250 its = &ipc_table_requests[0];
251 else
252 its = otable->ipr_size + 1;
253
254 if (target_size != ITS_SIZE_NONE) {
255 if ((otable != IPR_NULL) &&
256 (target_size <= otable->ipr_size->its_size)) {
257 ip_unlock(port);
258 return KERN_SUCCESS;
259 }
260 while ((its->its_size) && (its->its_size < target_size)) {
261 its++;
262 }
263 if (its->its_size == 0) {
264 ip_unlock(port);
265 return KERN_NO_SPACE;
266 }
267 }
268
269 ip_reference(port);
270 ip_unlock(port);
271
272 if ((its->its_size == 0) ||
273 ((ntable = it_requests_alloc(its)) == IPR_NULL)) {
274 ip_release(port);
275 return KERN_RESOURCE_SHORTAGE;
276 }
277
278 ip_lock(port);
279
280 /*
281 * Check that port is still active and that nobody else
282 * has slipped in and grown the table on us. Note that
283 * just checking if the current table pointer == otable
284 * isn't sufficient; must check ipr_size.
285 */
286
287 if (ip_active(port) && (port->ip_requests == otable) &&
288 ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) {
289 ipc_table_size_t oits;
290 ipc_table_elems_t osize, nsize;
291 ipc_port_request_index_t free, i;
292
293 /* copy old table to new table */
294
295 if (otable != IPR_NULL) {
296 oits = otable->ipr_size;
297 osize = oits->its_size;
298 free = otable->ipr_next;
299
300 (void) memcpy((void *)(ntable + 1),
301 (const void *)(otable + 1),
302 (osize - 1) * sizeof(struct ipc_port_request));
303 } else {
304 osize = 1;
305 oits = 0;
306 free = 0;
307 }
308
309 nsize = its->its_size;
310 assert(nsize > osize);
311
312 /* add new elements to the new table's free list */
313
314 for (i = osize; i < nsize; i++) {
315 ipc_port_request_t ipr = &ntable[i];
316
317 ipr->ipr_name = MACH_PORT_NULL;
318 ipr->ipr_next = free;
319 free = i;
320 }
321
322 ntable->ipr_next = free;
323 ntable->ipr_size = its;
324 port->ip_requests = ntable;
325 ip_unlock(port);
326 ip_release(port);
327
328 if (otable != IPR_NULL) {
329 it_requests_free(oits, otable);
330 }
331 } else {
332 ip_unlock(port);
333 ip_release(port);
334 it_requests_free(its, ntable);
335 }
336
337 return KERN_SUCCESS;
338 }
339
340 /*
341 * Routine: ipc_port_request_sparm
342 * Purpose:
343 * Arm delayed send-possible request.
344 * Conditions:
345 * The port must be locked and active.
346 *
347 * Returns TRUE if the request was armed
348 * (or armed with importance in that version).
349 */
350
351 #if IMPORTANCE_INHERITANCE
352 boolean_t
353 ipc_port_request_sparm(
354 ipc_port_t port,
355 __assert_only mach_port_name_t name,
356 ipc_port_request_index_t index,
357 mach_msg_option_t option)
358 #else
359 boolean_t
360 ipc_port_request_sparm(
361 ipc_port_t port,
362 __assert_only mach_port_name_t name,
363 ipc_port_request_index_t index)
364 #endif /* IMPORTANCE_INHERITANCE */
365 {
366 if (index != IE_REQ_NONE) {
367 ipc_port_request_t ipr, table;
368
369 assert(ip_active(port));
370
371 table = port->ip_requests;
372 assert(table != IPR_NULL);
373
374 ipr = &table[index];
375 assert(ipr->ipr_name == name);
376
377 if (IPR_SOR_SPREQ(ipr->ipr_soright)) {
378 ipr->ipr_soright = IPR_SOR_MAKE(ipr->ipr_soright, IPR_SOR_SPARM_MASK);
379 port->ip_sprequests = 1;
380 #if IMPORTANCE_INHERITANCE
381 if (((option & MACH_SEND_NOIMPORTANCE) == 0) &&
382 (port->ip_impdonation != 0) &&
383 (port->ip_spimportant == 0) &&
384 (((option & MACH_SEND_IMPORTANCE) != 0) ||
385 (task_is_importance_donor(current_task())))) {
386 port->ip_spimportant = 1;
387 return TRUE;
388 }
389 #else
390 return TRUE;
391 #endif /* IMPORTANCE_INHERITANCE */
392 }
393 }
394 return FALSE;
395 }
396
397 /*
398 * Routine: ipc_port_request_type
399 * Purpose:
400 * Determine the type(s) of port requests enabled for a name.
401 * Conditions:
402 * The port must be locked or inactive (to avoid table growth).
403 * The index must not be IE_REQ_NONE and for the name in question.
404 */
405 mach_port_type_t
406 ipc_port_request_type(
407 ipc_port_t port,
408 __assert_only mach_port_name_t name,
409 ipc_port_request_index_t index)
410 {
411 ipc_port_request_t ipr, table;
412 mach_port_type_t type = 0;
413
414 table = port->ip_requests;
415 assert (table != IPR_NULL);
416
417 assert(index != IE_REQ_NONE);
418 ipr = &table[index];
419 assert(ipr->ipr_name == name);
420
421 if (IP_VALID(IPR_SOR_PORT(ipr->ipr_soright))) {
422 type |= MACH_PORT_TYPE_DNREQUEST;
423
424 if (IPR_SOR_SPREQ(ipr->ipr_soright)) {
425 type |= MACH_PORT_TYPE_SPREQUEST;
426
427 if (!IPR_SOR_SPARMED(ipr->ipr_soright)) {
428 type |= MACH_PORT_TYPE_SPREQUEST_DELAYED;
429 }
430 }
431 }
432 return type;
433 }
434
435 /*
436 * Routine: ipc_port_request_cancel
437 * Purpose:
438 * Cancel a dead-name/send-possible request and return the send-once right.
439 * Conditions:
440 * The port must be locked and active.
441 * The index must not be IPR_REQ_NONE and must correspond with name.
442 */
443
444 ipc_port_t
445 ipc_port_request_cancel(
446 ipc_port_t port,
447 __assert_only mach_port_name_t name,
448 ipc_port_request_index_t index)
449 {
450 ipc_port_request_t ipr, table;
451 ipc_port_t request = IP_NULL;
452
453 assert(ip_active(port));
454 table = port->ip_requests;
455 assert(table != IPR_NULL);
456
457 assert (index != IE_REQ_NONE);
458 ipr = &table[index];
459 assert(ipr->ipr_name == name);
460 request = IPR_SOR_PORT(ipr->ipr_soright);
461
462 /* return ipr to the free list inside the table */
463 ipr->ipr_name = MACH_PORT_NULL;
464 ipr->ipr_next = table->ipr_next;
465 table->ipr_next = index;
466
467 return request;
468 }
469
470 /*
471 * Routine: ipc_port_pdrequest
472 * Purpose:
473 * Make a port-deleted request, returning the
474 * previously registered send-once right.
475 * Just cancels the previous request if notify is IP_NULL.
476 * Conditions:
477 * The port is locked and active. It is unlocked.
478 * Consumes a ref for notify (if non-null), and
479 * returns previous with a ref (if non-null).
480 */
481
482 void
483 ipc_port_pdrequest(
484 ipc_port_t port,
485 ipc_port_t notify,
486 ipc_port_t *previousp)
487 {
488 ipc_port_t previous;
489
490 assert(ip_active(port));
491
492 previous = port->ip_pdrequest;
493 port->ip_pdrequest = notify;
494 ip_unlock(port);
495
496 *previousp = previous;
497 }
498
499 /*
500 * Routine: ipc_port_nsrequest
501 * Purpose:
502 * Make a no-senders request, returning the
503 * previously registered send-once right.
504 * Just cancels the previous request if notify is IP_NULL.
505 * Conditions:
506 * The port is locked and active. It is unlocked.
507 * Consumes a ref for notify (if non-null), and
508 * returns previous with a ref (if non-null).
509 */
510
511 void
512 ipc_port_nsrequest(
513 ipc_port_t port,
514 mach_port_mscount_t sync,
515 ipc_port_t notify,
516 ipc_port_t *previousp)
517 {
518 ipc_port_t previous;
519 mach_port_mscount_t mscount;
520
521 assert(ip_active(port));
522
523 previous = port->ip_nsrequest;
524 mscount = port->ip_mscount;
525
526 if ((port->ip_srights == 0) && (sync <= mscount) &&
527 (notify != IP_NULL)) {
528 port->ip_nsrequest = IP_NULL;
529 ip_unlock(port);
530 ipc_notify_no_senders(notify, mscount);
531 } else {
532 port->ip_nsrequest = notify;
533 ip_unlock(port);
534 }
535
536 *previousp = previous;
537 }
538
539
540 /*
541 * Routine: ipc_port_clear_receiver
542 * Purpose:
543 * Prepares a receive right for transmission/destruction.
544 * Conditions:
545 * The port is locked and active.
546 */
547
548 void
549 ipc_port_clear_receiver(
550 ipc_port_t port)
551 {
552 spl_t s;
553
554 assert(ip_active(port));
555
556 /*
557 * pull ourselves from any sets.
558 */
559 if (port->ip_in_pset != 0) {
560 ipc_pset_remove_from_all(port);
561 assert(port->ip_in_pset == 0);
562 }
563
564 /*
565 * Send anyone waiting on the port's queue directly away.
566 * Also clear the mscount and seqno.
567 */
568 s = splsched();
569 imq_lock(&port->ip_messages);
570 ipc_mqueue_changed(&port->ip_messages);
571 ipc_port_set_mscount(port, 0);
572 port->ip_messages.imq_seqno = 0;
573 port->ip_context = port->ip_guarded = port->ip_strict_guard = 0;
574 imq_unlock(&port->ip_messages);
575 splx(s);
576 }
577
578 /*
579 * Routine: ipc_port_init
580 * Purpose:
581 * Initializes a newly-allocated port.
582 * Doesn't touch the ip_object fields.
583 */
584
585 void
586 ipc_port_init(
587 ipc_port_t port,
588 ipc_space_t space,
589 mach_port_name_t name)
590 {
591 /* port->ip_kobject doesn't have to be initialized */
592
593 port->ip_receiver = space;
594 port->ip_receiver_name = name;
595
596 port->ip_mscount = 0;
597 port->ip_srights = 0;
598 port->ip_sorights = 0;
599
600 port->ip_nsrequest = IP_NULL;
601 port->ip_pdrequest = IP_NULL;
602 port->ip_requests = IPR_NULL;
603
604 port->ip_premsg = IKM_NULL;
605 port->ip_context = 0;
606
607 port->ip_sprequests = 0;
608 port->ip_spimportant = 0;
609 port->ip_impdonation = 0;
610 port->ip_tempowner = 0;
611
612 port->ip_guarded = 0;
613 port->ip_strict_guard = 0;
614 port->ip_impcount = 0;
615
616 port->ip_reserved = 0;
617
618 ipc_mqueue_init(&port->ip_messages,
619 FALSE /* !set */, NULL /* no reserved link */);
620 }
621
622 /*
623 * Routine: ipc_port_alloc
624 * Purpose:
625 * Allocate a port.
626 * Conditions:
627 * Nothing locked. If successful, the port is returned
628 * locked. (The caller doesn't have a reference.)
629 * Returns:
630 * KERN_SUCCESS The port is allocated.
631 * KERN_INVALID_TASK The space is dead.
632 * KERN_NO_SPACE No room for an entry in the space.
633 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
634 */
635
636 kern_return_t
637 ipc_port_alloc(
638 ipc_space_t space,
639 mach_port_name_t *namep,
640 ipc_port_t *portp)
641 {
642 ipc_port_t port;
643 mach_port_name_t name;
644 kern_return_t kr;
645
646 #if MACH_ASSERT
647 uintptr_t buf[IP_CALLSTACK_MAX];
648 ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
649 #endif /* MACH_ASSERT */
650
651 kr = ipc_object_alloc(space, IOT_PORT,
652 MACH_PORT_TYPE_RECEIVE, 0,
653 &name, (ipc_object_t *) &port);
654 if (kr != KERN_SUCCESS)
655 return kr;
656
657 /* port and space are locked */
658 ipc_port_init(port, space, name);
659
660 #if MACH_ASSERT
661 ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
662 #endif /* MACH_ASSERT */
663
664 /* unlock space after init */
665 is_write_unlock(space);
666
667 *namep = name;
668 *portp = port;
669
670 return KERN_SUCCESS;
671 }
672
673 /*
674 * Routine: ipc_port_alloc_name
675 * Purpose:
676 * Allocate a port, with a specific name.
677 * Conditions:
678 * Nothing locked. If successful, the port is returned
679 * locked. (The caller doesn't have a reference.)
680 * Returns:
681 * KERN_SUCCESS The port is allocated.
682 * KERN_INVALID_TASK The space is dead.
683 * KERN_NAME_EXISTS The name already denotes a right.
684 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
685 */
686
687 kern_return_t
688 ipc_port_alloc_name(
689 ipc_space_t space,
690 mach_port_name_t name,
691 ipc_port_t *portp)
692 {
693 ipc_port_t port;
694 kern_return_t kr;
695
696 #if MACH_ASSERT
697 uintptr_t buf[IP_CALLSTACK_MAX];
698 ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
699 #endif /* MACH_ASSERT */
700
701 kr = ipc_object_alloc_name(space, IOT_PORT,
702 MACH_PORT_TYPE_RECEIVE, 0,
703 name, (ipc_object_t *) &port);
704 if (kr != KERN_SUCCESS)
705 return kr;
706
707 /* port is locked */
708
709 ipc_port_init(port, space, name);
710
711 #if MACH_ASSERT
712 ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
713 #endif /* MACH_ASSERT */
714
715 *portp = port;
716
717 return KERN_SUCCESS;
718 }
719
720 /*
721 * Routine: ipc_port_spnotify
722 * Purpose:
723 * Generate send-possible port notifications.
724 * Conditions:
725 * Nothing locked, reference held on port.
726 */
727 void
728 ipc_port_spnotify(
729 ipc_port_t port)
730 {
731 ipc_port_request_index_t index = 0;
732 ipc_table_elems_t size = 0;
733 #if IMPORTANCE_INHERITANCE
734 boolean_t dropassert = FALSE;
735 #endif /* IMPORTANCE_INHERITANCE */
736
737 /*
738 * If the port has no send-possible request
739 * armed, don't bother to lock the port.
740 */
741 if (port->ip_sprequests == 0)
742 return;
743
744 ip_lock(port);
745
746 #if IMPORTANCE_INHERITANCE
747 if (port->ip_spimportant != 0) {
748 port->ip_spimportant = 0;
749 if (ipc_port_impcount_delta(port, -1, IP_NULL) == -1) {
750 dropassert = TRUE;
751 }
752 }
753 #endif /* IMPORTANCE_INHERITANCE */
754
755 if (port->ip_sprequests == 0) {
756 ip_unlock(port);
757 goto out;
758 }
759 port->ip_sprequests = 0;
760
761 revalidate:
762 if (ip_active(port)) {
763 ipc_port_request_t requests;
764
765 /* table may change each time port unlocked (reload) */
766 requests = port->ip_requests;
767 assert(requests != IPR_NULL);
768
769 /*
770 * no need to go beyond table size when first
771 * we entered - those are future notifications.
772 */
773 if (size == 0)
774 size = requests->ipr_size->its_size;
775
776 /* no need to backtrack either */
777 while (++index < size) {
778 ipc_port_request_t ipr = &requests[index];
779 mach_port_name_t name = ipr->ipr_name;
780 ipc_port_t soright = IPR_SOR_PORT(ipr->ipr_soright);
781 boolean_t armed = IPR_SOR_SPARMED(ipr->ipr_soright);
782
783 if (MACH_PORT_VALID(name) && armed && IP_VALID(soright)) {
784 /* claim send-once right - slot still inuse */
785 ipr->ipr_soright = IP_NULL;
786 ip_unlock(port);
787
788 ipc_notify_send_possible(soright, name);
789
790 ip_lock(port);
791 goto revalidate;
792 }
793 }
794 }
795 ip_unlock(port);
796 out:
797 #if IMPORTANCE_INHERITANCE
798 if (dropassert == TRUE && ipc_importance_task_is_any_receiver_type(current_task()->task_imp_base)) {
799 /* drop internal assertion */
800 ipc_importance_task_drop_internal_assertion(current_task()->task_imp_base, 1);
801 }
802 #endif /* IMPORTANCE_INHERITANCE */
803 return;
804 }
805
806 /*
807 * Routine: ipc_port_dnnotify
808 * Purpose:
809 * Generate dead name notifications for
810 * all outstanding dead-name and send-
811 * possible requests.
812 * Conditions:
813 * Nothing locked.
814 * Port must be inactive.
815 * Reference held on port.
816 */
817 void
818 ipc_port_dnnotify(
819 ipc_port_t port)
820 {
821 ipc_port_request_t requests = port->ip_requests;
822
823 assert(!ip_active(port));
824 if (requests != IPR_NULL) {
825 ipc_table_size_t its = requests->ipr_size;
826 ipc_table_elems_t size = its->its_size;
827 ipc_port_request_index_t index;
828 for (index = 1; index < size; index++) {
829 ipc_port_request_t ipr = &requests[index];
830 mach_port_name_t name = ipr->ipr_name;
831 ipc_port_t soright = IPR_SOR_PORT(ipr->ipr_soright);
832
833 if (MACH_PORT_VALID(name) && IP_VALID(soright)) {
834 ipc_notify_dead_name(soright, name);
835 }
836 }
837 }
838 }
839
840
841 /*
842 * Routine: ipc_port_destroy
843 * Purpose:
844 * Destroys a port. Cleans up queued messages.
845 *
846 * If the port has a backup, it doesn't get destroyed,
847 * but is sent in a port-destroyed notification to the backup.
848 * Conditions:
849 * The port is locked and alive; nothing else locked.
850 * The caller has a reference, which is consumed.
851 * Afterwards, the port is unlocked and dead.
852 */
853
854 void
855 ipc_port_destroy(
856 ipc_port_t port)
857 {
858 ipc_port_t pdrequest, nsrequest;
859 ipc_mqueue_t mqueue;
860 ipc_kmsg_t kmsg;
861
862 #if IMPORTANCE_INHERITANCE
863 ipc_importance_task_t release_imp_task = IIT_NULL;
864 thread_t self = current_thread();
865 boolean_t top = (self->ith_assertions == 0);
866 natural_t assertcnt = 0;
867 #endif /* IMPORTANCE_INHERITANCE */
868
869 assert(ip_active(port));
870 /* port->ip_receiver_name is garbage */
871 /* port->ip_receiver/port->ip_destination is garbage */
872 assert(port->ip_in_pset == 0);
873 assert(port->ip_mscount == 0);
874
875 /* check for a backup port */
876 pdrequest = port->ip_pdrequest;
877
878 #if IMPORTANCE_INHERITANCE
879 /* determine how many assertions to drop and from whom */
880 if (port->ip_tempowner != 0) {
881 assert(top);
882 release_imp_task = port->ip_imp_task;
883 if (IIT_NULL != release_imp_task) {
884 port->ip_imp_task = IIT_NULL;
885 assertcnt = port->ip_impcount;
886 }
887 /* Otherwise, nothing to drop */
888 } else {
889 assertcnt = port->ip_impcount;
890 if (pdrequest != IP_NULL)
891 /* mark in limbo for the journey */
892 port->ip_tempowner = 1;
893 }
894
895 if (top)
896 self->ith_assertions = assertcnt;
897 #endif /* IMPORTANCE_INHERITANCE */
898
899 if (pdrequest != IP_NULL) {
900 /* we assume the ref for pdrequest */
901 port->ip_pdrequest = IP_NULL;
902
903 /* make port be in limbo */
904 port->ip_receiver_name = MACH_PORT_NULL;
905 port->ip_destination = IP_NULL;
906 ip_unlock(port);
907
908 /* consumes our refs for port and pdrequest */
909 ipc_notify_port_destroyed(pdrequest, port);
910
911 goto drop_assertions;
912 }
913
914 /* once port is dead, we don't need to keep it locked */
915
916 port->ip_object.io_bits &= ~IO_BITS_ACTIVE;
917 port->ip_timestamp = ipc_port_timestamp();
918 nsrequest = port->ip_nsrequest;
919
920 /*
921 * If the port has a preallocated message buffer and that buffer
922 * is not inuse, free it. If it has an inuse one, then the kmsg
923 * free will detect that we freed the association and it can free it
924 * like a normal buffer.
925 */
926 if (IP_PREALLOC(port)) {
927 ipc_port_t inuse_port;
928
929 kmsg = port->ip_premsg;
930 assert(kmsg != IKM_NULL);
931 inuse_port = ikm_prealloc_inuse_port(kmsg);
932 IP_CLEAR_PREALLOC(port, kmsg);
933 ip_unlock(port);
934 if (inuse_port != IP_NULL) {
935 assert(inuse_port == port);
936 } else {
937 ipc_kmsg_free(kmsg);
938 }
939 } else {
940 ip_unlock(port);
941 }
942
943 /* throw away no-senders request */
944 if (nsrequest != IP_NULL)
945 ipc_notify_send_once(nsrequest); /* consumes ref */
946
947 /* destroy any queued messages */
948 mqueue = &port->ip_messages;
949 ipc_mqueue_destroy(mqueue);
950
951 /* cleanup waitq related resources */
952 ipc_mqueue_deinit(mqueue);
953
954 /* generate dead-name notifications */
955 ipc_port_dnnotify(port);
956
957 ipc_kobject_destroy(port);
958
959 ip_release(port); /* consume caller's ref */
960
961 drop_assertions:
962 #if IMPORTANCE_INHERITANCE
963 if (release_imp_task != IIT_NULL) {
964 if (assertcnt > 0) {
965 assert(top);
966 self->ith_assertions = 0;
967 assert(ipc_importance_task_is_any_receiver_type(release_imp_task));
968 ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
969 }
970 ipc_importance_task_release(release_imp_task);
971
972 } else if (assertcnt > 0) {
973 if (top) {
974 self->ith_assertions = 0;
975 release_imp_task = current_task()->task_imp_base;
976 if (ipc_importance_task_is_any_receiver_type(release_imp_task)) {
977 ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
978 }
979 }
980 }
981 #endif /* IMPORTANCE_INHERITANCE */
982 }
983
984 /*
985 * Routine: ipc_port_check_circularity
986 * Purpose:
987 * Check if queueing "port" in a message for "dest"
988 * would create a circular group of ports and messages.
989 *
990 * If no circularity (FALSE returned), then "port"
991 * is changed from "in limbo" to "in transit".
992 *
993 * That is, we want to set port->ip_destination == dest,
994 * but guaranteeing that this doesn't create a circle
995 * port->ip_destination->ip_destination->... == port
996 *
997 * Additionally, if port was successfully changed to "in transit",
998 * propagate boost assertions from the "in limbo" port to all
999 * the ports in the chain, and, if the destination task accepts
1000 * boosts, to the destination task.
1001 *
1002 * Conditions:
1003 * No ports locked. References held for "port" and "dest".
1004 */
1005
1006 boolean_t
1007 ipc_port_check_circularity(
1008 ipc_port_t port,
1009 ipc_port_t dest)
1010 {
1011 ipc_port_t base;
1012
1013 #if IMPORTANCE_INHERITANCE
1014 ipc_importance_task_t imp_task = IIT_NULL;
1015 ipc_importance_task_t release_imp_task = IIT_NULL;
1016 int assertcnt = 0;
1017 #endif /* IMPORTANCE_INHERITANCE */
1018
1019 assert(port != IP_NULL);
1020 assert(dest != IP_NULL);
1021
1022 if (port == dest)
1023 return TRUE;
1024 base = dest;
1025
1026 /*
1027 * First try a quick check that can run in parallel.
1028 * No circularity if dest is not in transit.
1029 */
1030
1031 ip_lock(port);
1032 if (ip_lock_try(dest)) {
1033 if (!ip_active(dest) ||
1034 (dest->ip_receiver_name != MACH_PORT_NULL) ||
1035 (dest->ip_destination == IP_NULL))
1036 goto not_circular;
1037
1038 /* dest is in transit; further checking necessary */
1039
1040 ip_unlock(dest);
1041 }
1042 ip_unlock(port);
1043
1044 ipc_port_multiple_lock(); /* massive serialization */
1045
1046 /*
1047 * Search for the end of the chain (a port not in transit),
1048 * acquiring locks along the way.
1049 */
1050
1051 for (;;) {
1052 ip_lock(base);
1053
1054 if (!ip_active(base) ||
1055 (base->ip_receiver_name != MACH_PORT_NULL) ||
1056 (base->ip_destination == IP_NULL))
1057 break;
1058
1059 base = base->ip_destination;
1060 }
1061
1062 /* all ports in chain from dest to base, inclusive, are locked */
1063
1064 if (port == base) {
1065 /* circularity detected! */
1066
1067 ipc_port_multiple_unlock();
1068
1069 /* port (== base) is in limbo */
1070
1071 assert(ip_active(port));
1072 assert(port->ip_receiver_name == MACH_PORT_NULL);
1073 assert(port->ip_destination == IP_NULL);
1074
1075 while (dest != IP_NULL) {
1076 ipc_port_t next;
1077
1078 /* dest is in transit or in limbo */
1079
1080 assert(ip_active(dest));
1081 assert(dest->ip_receiver_name == MACH_PORT_NULL);
1082
1083 next = dest->ip_destination;
1084 ip_unlock(dest);
1085 dest = next;
1086 }
1087
1088 return TRUE;
1089 }
1090
1091 /*
1092 * The guarantee: lock port while the entire chain is locked.
1093 * Once port is locked, we can take a reference to dest,
1094 * add port to the chain, and unlock everything.
1095 */
1096
1097 ip_lock(port);
1098 ipc_port_multiple_unlock();
1099
1100 not_circular:
1101
1102 /* port is in limbo */
1103
1104 assert(ip_active(port));
1105 assert(port->ip_receiver_name == MACH_PORT_NULL);
1106 assert(port->ip_destination == IP_NULL);
1107
1108 ip_reference(dest);
1109 port->ip_destination = dest;
1110
1111 #if IMPORTANCE_INHERITANCE
1112 /* must have been in limbo or still bound to a task */
1113 assert(port->ip_tempowner != 0);
1114
1115 /*
1116 * We delayed dropping assertions from a specific task.
1117 * Cache that info now (we'll drop assertions and the
1118 * task reference below).
1119 */
1120 release_imp_task = port->ip_imp_task;
1121 if (IIT_NULL != release_imp_task) {
1122 port->ip_imp_task = IIT_NULL;
1123 }
1124 assertcnt = port->ip_impcount;
1125
1126 /* take the port out of limbo w.r.t. assertions */
1127 port->ip_tempowner = 0;
1128
1129 #endif /* IMPORTANCE_INHERITANCE */
1130
1131 /* now unlock chain */
1132
1133 ip_unlock(port);
1134
1135 for (;;) {
1136
1137 #if IMPORTANCE_INHERITANCE
1138 /* every port along chain track assertions behind it */
1139 dest->ip_impcount += assertcnt;
1140 #endif /* IMPORTANCE_INHERITANCE */
1141
1142 if (dest == base)
1143 break;
1144
1145 /* port is in transit */
1146
1147 assert(ip_active(dest));
1148 assert(dest->ip_receiver_name == MACH_PORT_NULL);
1149 assert(dest->ip_destination != IP_NULL);
1150
1151 #if IMPORTANCE_INHERITANCE
1152 assert(dest->ip_tempowner == 0);
1153 #endif /* IMPORTANCE_INHERITANCE */
1154
1155 port = dest->ip_destination;
1156 ip_unlock(dest);
1157 dest = port;
1158 }
1159
1160 /* base is not in transit */
1161 assert(!ip_active(base) ||
1162 (base->ip_receiver_name != MACH_PORT_NULL) ||
1163 (base->ip_destination == IP_NULL));
1164
1165 #if IMPORTANCE_INHERITANCE
1166 /*
1167 * Find the task to boost (if any).
1168 * We will boost "through" ports that don't know
1169 * about inheritance to deliver receive rights that
1170 * do.
1171 */
1172 if (ip_active(base) && (assertcnt > 0)) {
1173 if (base->ip_tempowner != 0) {
1174 if (IIT_NULL != base->ip_imp_task) {
1175 /* specified tempowner task */
1176 imp_task = base->ip_imp_task;
1177 assert(ipc_importance_task_is_any_receiver_type(imp_task));
1178 }
1179 /* otherwise don't boost current task */
1180
1181 } else if (base->ip_receiver_name != MACH_PORT_NULL) {
1182 ipc_space_t space = base->ip_receiver;
1183
1184 /* only spaces with boost-accepting tasks */
1185 if (space->is_task != TASK_NULL &&
1186 ipc_importance_task_is_any_receiver_type(space->is_task->task_imp_base))
1187 imp_task = space->is_task->task_imp_base;
1188 }
1189
1190 /* take reference before unlocking base */
1191 if (imp_task != IIT_NULL) {
1192 ipc_importance_task_reference(imp_task);
1193 }
1194 }
1195 #endif /* IMPORTANCE_INHERITANCE */
1196
1197 ip_unlock(base);
1198
1199 #if IMPORTANCE_INHERITANCE
1200 /*
1201 * Transfer assertions now that the ports are unlocked.
1202 * Avoid extra overhead if transferring to/from the same task.
1203 */
1204 boolean_t transfer_assertions = (imp_task != release_imp_task) ? TRUE : FALSE;
1205
1206 if (imp_task != IIT_NULL) {
1207 if (transfer_assertions)
1208 ipc_importance_task_hold_internal_assertion(imp_task, assertcnt);
1209 ipc_importance_task_release(imp_task);
1210 imp_task = IIT_NULL;
1211 }
1212
1213 if (release_imp_task != IIT_NULL) {
1214 if (transfer_assertions)
1215 ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
1216 ipc_importance_task_release(release_imp_task);
1217 release_imp_task = IIT_NULL;
1218 }
1219 #endif /* IMPORTANCE_INHERITANCE */
1220
1221 return FALSE;
1222 }
1223
1224 /*
1225 * Routine: ipc_port_impcount_delta
1226 * Purpose:
1227 * Adjust only the importance count associated with a port.
1228 * If there are any adjustments to be made to receiver task,
1229 * those are handled elsewhere.
1230 *
1231 * For now, be defensive during deductions to make sure the
1232 * impcount for the port doesn't underflow zero. This will
1233 * go away when the port boost addition is made atomic (see
1234 * note in ipc_port_importance_delta()).
1235 * Conditions:
1236 * The port is referenced and locked.
1237 * Nothing else is locked.
1238 */
1239 mach_port_delta_t
1240 ipc_port_impcount_delta(
1241 ipc_port_t port,
1242 mach_port_delta_t delta,
1243 ipc_port_t __unused base)
1244 {
1245 mach_port_delta_t absdelta;
1246
1247 if (!ip_active(port)) {
1248 return 0;
1249 }
1250
1251 /* adding/doing nothing is easy */
1252 if (delta >= 0) {
1253 port->ip_impcount += delta;
1254 return delta;
1255 }
1256
1257 absdelta = 0 - delta;
1258 //assert(port->ip_impcount >= absdelta);
1259 /* if we have enough to deduct, we're done */
1260 if (port->ip_impcount >= absdelta) {
1261 port->ip_impcount -= absdelta;
1262 return delta;
1263 }
1264
1265 #if DEVELOPMENT || DEBUG
1266 if (port->ip_receiver_name != MACH_PORT_NULL) {
1267 task_t target_task = port->ip_receiver->is_task;
1268 ipc_importance_task_t target_imp = target_task->task_imp_base;
1269 const char *target_procname;
1270 int target_pid;
1271
1272 if (target_imp != IIT_NULL) {
1273 target_procname = target_imp->iit_procname;
1274 target_pid = target_imp->iit_bsd_pid;
1275 } else {
1276 target_procname = "unknown";
1277 target_pid = -1;
1278 }
1279 printf("Over-release of importance assertions for port 0x%x receiver pid %d (%s), "
1280 "dropping %d assertion(s) but port only has %d remaining.\n",
1281 port->ip_receiver_name,
1282 target_imp->iit_bsd_pid, target_imp->iit_procname,
1283 absdelta, port->ip_impcount);
1284
1285 } else if (base != IP_NULL) {
1286 task_t target_task = base->ip_receiver->is_task;
1287 ipc_importance_task_t target_imp = target_task->task_imp_base;
1288 const char *target_procname;
1289 int target_pid;
1290
1291 if (target_imp != IIT_NULL) {
1292 target_procname = target_imp->iit_procname;
1293 target_pid = target_imp->iit_bsd_pid;
1294 } else {
1295 target_procname = "unknown";
1296 target_pid = -1;
1297 }
1298 printf("Over-release of importance assertions for port %p "
1299 "enqueued on port 0x%x with receiver pid %d (%s), "
1300 "dropping %d assertion(s) but port only has %d remaining.\n",
1301 port, base->ip_receiver_name,
1302 target_imp->iit_bsd_pid, target_imp->iit_procname,
1303 absdelta, port->ip_impcount);
1304 }
1305 #endif
1306 delta = 0 - port->ip_impcount;
1307 port->ip_impcount = 0;
1308 return delta;
1309 }
1310
1311 /*
1312 * Routine: ipc_port_importance_delta_internal
1313 * Purpose:
1314 * Adjust the importance count through the given port.
1315 * If the port is in transit, apply the delta throughout
1316 * the chain. Determine if the there is a task at the
1317 * base of the chain that wants/needs to be adjusted,
1318 * and if so, apply the delta.
1319 * Conditions:
1320 * The port is referenced and locked on entry.
1321 * Nothing else is locked.
1322 * The lock may be dropped on exit.
1323 * Returns TRUE if lock was dropped.
1324 */
1325 #if IMPORTANCE_INHERITANCE
1326
1327 boolean_t
1328 ipc_port_importance_delta_internal(
1329 ipc_port_t port,
1330 mach_port_delta_t *deltap,
1331 ipc_importance_task_t *imp_task)
1332 {
1333 ipc_port_t next, base;
1334 boolean_t dropped = FALSE;
1335
1336 *imp_task = IIT_NULL;
1337
1338 if (*deltap == 0)
1339 return FALSE;
1340
1341 base = port;
1342
1343 /* if port is in transit, have to search for end of chain */
1344 if (ip_active(port) &&
1345 port->ip_destination != IP_NULL &&
1346 port->ip_receiver_name == MACH_PORT_NULL) {
1347
1348 dropped = TRUE;
1349
1350 ip_unlock(port);
1351 ipc_port_multiple_lock(); /* massive serialization */
1352 ip_lock(base);
1353
1354 while(ip_active(base) &&
1355 base->ip_destination != IP_NULL &&
1356 base->ip_receiver_name == MACH_PORT_NULL) {
1357
1358 base = base->ip_destination;
1359 ip_lock(base);
1360 }
1361 ipc_port_multiple_unlock();
1362 }
1363
1364 /* unlock down to the base, adding a boost at each level */
1365 for (;;) {
1366 /*
1367 * JMM TODO - because of the port unlock to grab the multiple lock
1368 * above, a subsequent drop of importance could race and beat
1369 * the "previous" increase - causing the port impcount to go
1370 * negative briefly. The defensive deduction performed by
1371 * ipc_port_impcount_delta() defeats that, and therefore can
1372 * cause an importance leak once the increase finally arrives.
1373 *
1374 * Need to rework the importance delta logic to be more like
1375 * ipc_importance_inherit_from() where it locks all it needs in
1376 * one pass to avoid any lock drops - to keep that race from
1377 * ever occuring.
1378 */
1379 *deltap = ipc_port_impcount_delta(port, *deltap, base);
1380
1381 if (port == base) {
1382 break;
1383 }
1384
1385 /* port is in transit */
1386 assert(port->ip_tempowner == 0);
1387 next = port->ip_destination;
1388 ip_unlock(port);
1389 port = next;
1390 }
1391
1392 /* find the task (if any) to boost according to the base */
1393 if (ip_active(base)) {
1394 if (base->ip_tempowner != 0) {
1395 if (IIT_NULL != base->ip_imp_task)
1396 *imp_task = base->ip_imp_task;
1397 /* otherwise don't boost */
1398
1399 } else if (base->ip_receiver_name != MACH_PORT_NULL) {
1400 ipc_space_t space = base->ip_receiver;
1401
1402 /* only spaces with boost-accepting tasks */
1403 if (space->is_task != TASK_NULL &&
1404 ipc_importance_task_is_any_receiver_type(space->is_task->task_imp_base)) {
1405 *imp_task = space->is_task->task_imp_base;
1406 }
1407 }
1408 }
1409
1410 /*
1411 * Only the base is locked. If we have to hold or drop task
1412 * importance assertions, we'll have to drop that lock as well.
1413 */
1414 if (*imp_task != IIT_NULL) {
1415 /* take a reference before unlocking base */
1416 ipc_importance_task_reference(*imp_task);
1417 }
1418
1419 if (dropped == TRUE) {
1420 ip_unlock(base);
1421 }
1422
1423 return dropped;
1424 }
1425 #endif /* IMPORTANCE_INHERITANCE */
1426
1427 /*
1428 * Routine: ipc_port_importance_delta
1429 * Purpose:
1430 * Adjust the importance count through the given port.
1431 * If the port is in transit, apply the delta throughout
1432 * the chain.
1433 *
1434 * If there is a task at the base of the chain that wants/needs
1435 * to be adjusted, apply the delta.
1436 * Conditions:
1437 * The port is referenced and locked on entry.
1438 * Nothing else is locked.
1439 * The lock may be dropped on exit.
1440 * Returns TRUE if lock was dropped.
1441 */
1442 #if IMPORTANCE_INHERITANCE
1443
1444 boolean_t
1445 ipc_port_importance_delta(
1446 ipc_port_t port,
1447 mach_port_delta_t delta)
1448 {
1449 ipc_importance_task_t imp_task = IIT_NULL;
1450 boolean_t dropped;
1451
1452 dropped = ipc_port_importance_delta_internal(port, &delta, &imp_task);
1453
1454 if (IIT_NULL == imp_task)
1455 return dropped;
1456
1457 if (!dropped) {
1458 dropped = TRUE;
1459 ip_unlock(port);
1460 }
1461
1462 assert(ipc_importance_task_is_any_receiver_type(imp_task));
1463
1464 if (delta > 0)
1465 ipc_importance_task_hold_internal_assertion(imp_task, delta);
1466 else
1467 ipc_importance_task_drop_internal_assertion(imp_task, -delta);
1468
1469 ipc_importance_task_release(imp_task);
1470 return dropped;
1471 }
1472 #endif /* IMPORTANCE_INHERITANCE */
1473
1474 /*
1475 * Routine: ipc_port_lookup_notify
1476 * Purpose:
1477 * Make a send-once notify port from a receive right.
1478 * Returns IP_NULL if name doesn't denote a receive right.
1479 * Conditions:
1480 * The space must be locked (read or write) and active.
1481 * Being the active space, we can rely on thread server_id
1482 * context to give us the proper server level sub-order
1483 * within the space.
1484 */
1485
1486 ipc_port_t
1487 ipc_port_lookup_notify(
1488 ipc_space_t space,
1489 mach_port_name_t name)
1490 {
1491 ipc_port_t port;
1492 ipc_entry_t entry;
1493
1494 assert(is_active(space));
1495
1496 entry = ipc_entry_lookup(space, name);
1497 if (entry == IE_NULL)
1498 return IP_NULL;
1499 if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)
1500 return IP_NULL;
1501
1502 __IGNORE_WCASTALIGN(port = (ipc_port_t) entry->ie_object);
1503 assert(port != IP_NULL);
1504
1505 ip_lock(port);
1506 assert(ip_active(port));
1507 assert(port->ip_receiver_name == name);
1508 assert(port->ip_receiver == space);
1509
1510 ip_reference(port);
1511 port->ip_sorights++;
1512 ip_unlock(port);
1513
1514 return port;
1515 }
1516
1517 /*
1518 * Routine: ipc_port_make_send_locked
1519 * Purpose:
1520 * Make a naked send right from a receive right.
1521 *
1522 * Conditions:
1523 * port locked and active.
1524 */
1525 ipc_port_t
1526 ipc_port_make_send_locked(
1527 ipc_port_t port)
1528 {
1529 assert(ip_active(port));
1530 port->ip_mscount++;
1531 port->ip_srights++;
1532 ip_reference(port);
1533 return port;
1534 }
1535
1536 /*
1537 * Routine: ipc_port_make_send
1538 * Purpose:
1539 * Make a naked send right from a receive right.
1540 */
1541
1542 ipc_port_t
1543 ipc_port_make_send(
1544 ipc_port_t port)
1545 {
1546
1547 if (!IP_VALID(port))
1548 return port;
1549
1550 ip_lock(port);
1551 if (ip_active(port)) {
1552 port->ip_mscount++;
1553 port->ip_srights++;
1554 ip_reference(port);
1555 ip_unlock(port);
1556 return port;
1557 }
1558 ip_unlock(port);
1559 return IP_DEAD;
1560 }
1561
1562 /*
1563 * Routine: ipc_port_copy_send
1564 * Purpose:
1565 * Make a naked send right from another naked send right.
1566 * IP_NULL -> IP_NULL
1567 * IP_DEAD -> IP_DEAD
1568 * dead port -> IP_DEAD
1569 * live port -> port + ref
1570 * Conditions:
1571 * Nothing locked except possibly a space.
1572 */
1573
1574 ipc_port_t
1575 ipc_port_copy_send(
1576 ipc_port_t port)
1577 {
1578 ipc_port_t sright;
1579
1580 if (!IP_VALID(port))
1581 return port;
1582
1583 ip_lock(port);
1584 if (ip_active(port)) {
1585 assert(port->ip_srights > 0);
1586
1587 ip_reference(port);
1588 port->ip_srights++;
1589 sright = port;
1590 } else
1591 sright = IP_DEAD;
1592 ip_unlock(port);
1593
1594 return sright;
1595 }
1596
1597 /*
1598 * Routine: ipc_port_copyout_send
1599 * Purpose:
1600 * Copyout a naked send right (possibly null/dead),
1601 * or if that fails, destroy the right.
1602 * Conditions:
1603 * Nothing locked.
1604 */
1605
1606 mach_port_name_t
1607 ipc_port_copyout_send(
1608 ipc_port_t sright,
1609 ipc_space_t space)
1610 {
1611 mach_port_name_t name;
1612
1613 if (IP_VALID(sright)) {
1614 kern_return_t kr;
1615
1616 kr = ipc_object_copyout(space, (ipc_object_t) sright,
1617 MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
1618 if (kr != KERN_SUCCESS) {
1619 ipc_port_release_send(sright);
1620
1621 if (kr == KERN_INVALID_CAPABILITY)
1622 name = MACH_PORT_DEAD;
1623 else
1624 name = MACH_PORT_NULL;
1625 }
1626 } else
1627 name = CAST_MACH_PORT_TO_NAME(sright);
1628
1629 return name;
1630 }
1631
1632 /*
1633 * Routine: ipc_port_release_send
1634 * Purpose:
1635 * Release a naked send right.
1636 * Consumes a ref for the port.
1637 * Conditions:
1638 * Nothing locked.
1639 */
1640
1641 void
1642 ipc_port_release_send(
1643 ipc_port_t port)
1644 {
1645 ipc_port_t nsrequest = IP_NULL;
1646 mach_port_mscount_t mscount;
1647
1648 if (!IP_VALID(port))
1649 return;
1650
1651 ip_lock(port);
1652
1653 assert(port->ip_srights > 0);
1654 port->ip_srights--;
1655
1656 if (!ip_active(port)) {
1657 ip_unlock(port);
1658 ip_release(port);
1659 return;
1660 }
1661
1662 if (port->ip_srights == 0 &&
1663 port->ip_nsrequest != IP_NULL) {
1664 nsrequest = port->ip_nsrequest;
1665 port->ip_nsrequest = IP_NULL;
1666 mscount = port->ip_mscount;
1667 ip_unlock(port);
1668 ip_release(port);
1669 ipc_notify_no_senders(nsrequest, mscount);
1670 } else {
1671 ip_unlock(port);
1672 ip_release(port);
1673 }
1674 }
1675
1676 /*
1677 * Routine: ipc_port_make_sonce_locked
1678 * Purpose:
1679 * Make a naked send-once right from a receive right.
1680 * Conditions:
1681 * The port is locked and active.
1682 */
1683
1684 ipc_port_t
1685 ipc_port_make_sonce_locked(
1686 ipc_port_t port)
1687 {
1688 assert(ip_active(port));
1689 port->ip_sorights++;
1690 ip_reference(port);
1691 return port;
1692 }
1693
1694 /*
1695 * Routine: ipc_port_make_sonce
1696 * Purpose:
1697 * Make a naked send-once right from a receive right.
1698 * Conditions:
1699 * The port is not locked.
1700 */
1701
1702 ipc_port_t
1703 ipc_port_make_sonce(
1704 ipc_port_t port)
1705 {
1706 if (!IP_VALID(port))
1707 return port;
1708
1709 ip_lock(port);
1710 if (ip_active(port)) {
1711 port->ip_sorights++;
1712 ip_reference(port);
1713 ip_unlock(port);
1714 return port;
1715 }
1716 ip_unlock(port);
1717 return IP_DEAD;
1718 }
1719
1720 /*
1721 * Routine: ipc_port_release_sonce
1722 * Purpose:
1723 * Release a naked send-once right.
1724 * Consumes a ref for the port.
1725 *
1726 * In normal situations, this is never used.
1727 * Send-once rights are only consumed when
1728 * a message (possibly a send-once notification)
1729 * is sent to them.
1730 * Conditions:
1731 * Nothing locked except possibly a space.
1732 */
1733
1734 void
1735 ipc_port_release_sonce(
1736 ipc_port_t port)
1737 {
1738 if (!IP_VALID(port))
1739 return;
1740
1741 ip_lock(port);
1742
1743 assert(port->ip_sorights > 0);
1744
1745 port->ip_sorights--;
1746
1747 ip_unlock(port);
1748 ip_release(port);
1749 }
1750
1751 /*
1752 * Routine: ipc_port_release_receive
1753 * Purpose:
1754 * Release a naked (in limbo or in transit) receive right.
1755 * Consumes a ref for the port; destroys the port.
1756 * Conditions:
1757 * Nothing locked.
1758 */
1759
1760 void
1761 ipc_port_release_receive(
1762 ipc_port_t port)
1763 {
1764 ipc_port_t dest;
1765
1766 if (!IP_VALID(port))
1767 return;
1768
1769 ip_lock(port);
1770 assert(ip_active(port));
1771 assert(port->ip_receiver_name == MACH_PORT_NULL);
1772 dest = port->ip_destination;
1773
1774 ipc_port_destroy(port); /* consumes ref, unlocks */
1775
1776 if (dest != IP_NULL)
1777 ip_release(dest);
1778 }
1779
1780 /*
1781 * Routine: ipc_port_alloc_special
1782 * Purpose:
1783 * Allocate a port in a special space.
1784 * The new port is returned with one ref.
1785 * If unsuccessful, IP_NULL is returned.
1786 * Conditions:
1787 * Nothing locked.
1788 */
1789
1790 ipc_port_t
1791 ipc_port_alloc_special(
1792 ipc_space_t space)
1793 {
1794 ipc_port_t port;
1795
1796 __IGNORE_WCASTALIGN(port = (ipc_port_t) io_alloc(IOT_PORT));
1797 if (port == IP_NULL)
1798 return IP_NULL;
1799
1800 #if MACH_ASSERT
1801 uintptr_t buf[IP_CALLSTACK_MAX];
1802 ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
1803 #endif /* MACH_ASSERT */
1804
1805 bzero((char *)port, sizeof(*port));
1806 io_lock_init(&port->ip_object);
1807 port->ip_references = 1;
1808 port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0);
1809
1810 ipc_port_init(port, space, 1);
1811
1812 #if MACH_ASSERT
1813 ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
1814 #endif /* MACH_ASSERT */
1815
1816 return port;
1817 }
1818
1819 /*
1820 * Routine: ipc_port_dealloc_special
1821 * Purpose:
1822 * Deallocate a port in a special space.
1823 * Consumes one ref for the port.
1824 * Conditions:
1825 * Nothing locked.
1826 */
1827
1828 void
1829 ipc_port_dealloc_special(
1830 ipc_port_t port,
1831 __assert_only ipc_space_t space)
1832 {
1833 ip_lock(port);
1834 assert(ip_active(port));
1835 // assert(port->ip_receiver_name != MACH_PORT_NULL);
1836 assert(port->ip_receiver == space);
1837
1838 /*
1839 * We clear ip_receiver_name and ip_receiver to simplify
1840 * the ipc_space_kernel check in ipc_mqueue_send.
1841 */
1842
1843 port->ip_receiver_name = MACH_PORT_NULL;
1844 port->ip_receiver = IS_NULL;
1845
1846 /* relevant part of ipc_port_clear_receiver */
1847 ipc_port_set_mscount(port, 0);
1848 port->ip_messages.imq_seqno = 0;
1849
1850 ipc_port_destroy(port);
1851 }
1852
1853 /*
1854 * Routine: ipc_port_finalize
1855 * Purpose:
1856 * Called on last reference deallocate to
1857 * free any remaining data associated with the
1858 * port.
1859 * Conditions:
1860 * Nothing locked.
1861 */
1862 void
1863 ipc_port_finalize(
1864 ipc_port_t port)
1865 {
1866 ipc_port_request_t requests = port->ip_requests;
1867
1868 assert(!ip_active(port));
1869 if (requests != IPR_NULL) {
1870 ipc_table_size_t its = requests->ipr_size;
1871 it_requests_free(its, requests);
1872 port->ip_requests = IPR_NULL;
1873 }
1874
1875 ipc_mqueue_deinit(&port->ip_messages);
1876
1877 #if MACH_ASSERT
1878 ipc_port_track_dealloc(port);
1879 #endif /* MACH_ASSERT */
1880 }
1881
1882 #if MACH_ASSERT
1883 #include <kern/machine.h>
1884
1885 /*
1886 * Keep a list of all allocated ports.
1887 * Allocation is intercepted via ipc_port_init;
1888 * deallocation is intercepted via io_free.
1889 */
1890 #if 0
1891 queue_head_t port_alloc_queue;
1892 lck_spin_t port_alloc_queue_lock;
1893 #endif
1894
1895 unsigned long port_count = 0;
1896 unsigned long port_count_warning = 20000;
1897 unsigned long port_timestamp = 0;
1898
1899 void db_port_stack_trace(
1900 ipc_port_t port);
1901 void db_ref(
1902 int refs);
1903 int db_port_walk(
1904 unsigned int verbose,
1905 unsigned int display,
1906 unsigned int ref_search,
1907 unsigned int ref_target);
1908
1909 /*
1910 * Initialize global state needed for run-time
1911 * port debugging.
1912 */
1913 void
1914 ipc_port_debug_init(void)
1915 {
1916 #if 0
1917 queue_init(&port_alloc_queue);
1918 lck_spin_init(&port_alloc_queue_lock, &ipc_lck_grp, &ipc_lck_attr);
1919 #endif
1920
1921 if (!PE_parse_boot_argn("ipc_portbt", &ipc_portbt, sizeof (ipc_portbt)))
1922 ipc_portbt = 0;
1923 }
1924
1925 #ifdef MACH_BSD
1926 extern int proc_pid(struct proc*);
1927 #endif /* MACH_BSD */
1928
1929 /*
1930 * Initialize all of the debugging state in a port.
1931 * Insert the port into a global list of all allocated ports.
1932 */
1933 void
1934 ipc_port_init_debug(
1935 ipc_port_t port,
1936 uintptr_t *callstack,
1937 unsigned int callstack_max)
1938 {
1939 unsigned int i;
1940
1941 port->ip_thread = current_thread();
1942 port->ip_timetrack = port_timestamp++;
1943 for (i = 0; i < callstack_max; ++i)
1944 port->ip_callstack[i] = callstack[i];
1945 for (i = 0; i < IP_NSPARES; ++i)
1946 port->ip_spares[i] = 0;
1947
1948 #ifdef MACH_BSD
1949 task_t task = current_task();
1950 if (task != TASK_NULL) {
1951 struct proc* proc = (struct proc*) get_bsdtask_info(task);
1952 if (proc)
1953 port->ip_spares[0] = proc_pid(proc);
1954 }
1955 #endif /* MACH_BSD */
1956
1957 #if 0
1958 lck_spin_lock(&port_alloc_queue_lock);
1959 ++port_count;
1960 if (port_count_warning > 0 && port_count >= port_count_warning)
1961 assert(port_count < port_count_warning);
1962 queue_enter(&port_alloc_queue, port, ipc_port_t, ip_port_links);
1963 lck_spin_unlock(&port_alloc_queue_lock);
1964 #endif
1965 }
1966
1967 /*
1968 * Routine: ipc_port_callstack_init_debug
1969 * Purpose:
1970 * Calls the machine-dependent routine to
1971 * fill in an array with up to IP_CALLSTACK_MAX
1972 * levels of return pc information
1973 * Conditions:
1974 * May block (via copyin)
1975 */
1976 void
1977 ipc_port_callstack_init_debug(
1978 uintptr_t *callstack,
1979 unsigned int callstack_max)
1980 {
1981 unsigned int i;
1982
1983 /* guarantee the callstack is initialized */
1984 for (i=0; i < callstack_max; i++)
1985 callstack[i] = 0;
1986
1987 if (ipc_portbt)
1988 machine_callstack(callstack, callstack_max);
1989 }
1990
1991 /*
1992 * Remove a port from the queue of allocated ports.
1993 * This routine should be invoked JUST prior to
1994 * deallocating the actual memory occupied by the port.
1995 */
1996 #if 1
1997 void
1998 ipc_port_track_dealloc(
1999 __unused ipc_port_t port)
2000 {
2001 }
2002 #else
2003 void
2004 ipc_port_track_dealloc(
2005 ipc_port_t port)
2006 {
2007 lck_spin_lock(&port_alloc_queue_lock);
2008 assert(port_count > 0);
2009 --port_count;
2010 queue_remove(&port_alloc_queue, port, ipc_port_t, ip_port_links);
2011 lck_spin_unlock(&port_alloc_queue_lock);
2012 }
2013 #endif
2014
2015
2016 #endif /* MACH_ASSERT */