]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_right.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_right.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_FREE_COPYRIGHT@
30 */
0a7de745 31/*
1c79356b
A
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
0a7de745 35 *
1c79356b
A
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.
0a7de745 41 *
1c79356b
A
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.
0a7de745 45 *
1c79356b 46 * Carnegie Mellon requests users of this software to return to
0a7de745 47 *
1c79356b
A
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
0a7de745 52 *
1c79356b
A
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
2d21ac55
A
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 * Copyright (c) 2005-2006 SPARTA, Inc.
62 */
1c79356b
A
63/*
64 */
65/*
66 * File: ipc/ipc_right.c
67 * Author: Rich Draves
68 * Date: 1989
69 *
70 * Functions to manipulate IPC capabilities.
71 */
72
73#include <mach/boolean.h>
74#include <mach/kern_return.h>
75#include <mach/port.h>
76#include <mach/message.h>
77#include <kern/assert.h>
00867663 78#include <kern/ipc_kobject.h>
1c79356b 79#include <kern/misc_protos.h>
1c79356b
A
80#include <ipc/port.h>
81#include <ipc/ipc_entry.h>
82#include <ipc/ipc_space.h>
83#include <ipc/ipc_object.h>
84#include <ipc/ipc_hash.h>
85#include <ipc/ipc_port.h>
86#include <ipc/ipc_pset.h>
87#include <ipc/ipc_right.h>
88#include <ipc/ipc_notify.h>
89#include <ipc/ipc_table.h>
fe8ab488 90#include <ipc/ipc_importance.h>
2d21ac55 91#include <security/mac_mach_internal.h>
1c79356b
A
92
93/*
94 * Routine: ipc_right_lookup_write
95 * Purpose:
96 * Finds an entry in a space, given the name.
97 * Conditions:
98 * Nothing locked. If successful, the space is write-locked.
99 * Returns:
100 * KERN_SUCCESS Found an entry.
101 * KERN_INVALID_TASK The space is dead.
102 * KERN_INVALID_NAME Name doesn't exist in space.
103 */
104
105kern_return_t
106ipc_right_lookup_write(
0a7de745
A
107 ipc_space_t space,
108 mach_port_name_t name,
109 ipc_entry_t *entryp)
1c79356b
A
110{
111 ipc_entry_t entry;
112
113 assert(space != IS_NULL);
114
115 is_write_lock(space);
116
316670eb 117 if (!is_active(space)) {
1c79356b
A
118 is_write_unlock(space);
119 return KERN_INVALID_TASK;
120 }
121
122 if ((entry = ipc_entry_lookup(space, name)) == IE_NULL) {
123 is_write_unlock(space);
124 return KERN_INVALID_NAME;
125 }
126
127 *entryp = entry;
128 return KERN_SUCCESS;
129}
130
131/*
132 * Routine: ipc_right_lookup_two_write
133 * Purpose:
134 * Like ipc_right_lookup except that it returns two
135 * entries for two different names that were looked
136 * up under the same space lock.
137 * Conditions:
138 * Nothing locked. If successful, the space is write-locked.
139 * Returns:
140 * KERN_INVALID_TASK The space is dead.
141 * KERN_INVALID_NAME Name doesn't exist in space.
142 */
143
144kern_return_t
145ipc_right_lookup_two_write(
0a7de745
A
146 ipc_space_t space,
147 mach_port_name_t name1,
148 ipc_entry_t *entryp1,
149 mach_port_name_t name2,
150 ipc_entry_t *entryp2)
1c79356b
A
151{
152 ipc_entry_t entry1;
153 ipc_entry_t entry2;
154
155 assert(space != IS_NULL);
156
157 is_write_lock(space);
158
316670eb 159 if (!is_active(space)) {
1c79356b
A
160 is_write_unlock(space);
161 return KERN_INVALID_TASK;
162 }
163
164 if ((entry1 = ipc_entry_lookup(space, name1)) == IE_NULL) {
165 is_write_unlock(space);
d9a64523 166 mach_port_guard_exception(name1, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b
A
167 return KERN_INVALID_NAME;
168 }
169 if ((entry2 = ipc_entry_lookup(space, name2)) == IE_NULL) {
170 is_write_unlock(space);
d9a64523 171 mach_port_guard_exception(name2, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b
A
172 return KERN_INVALID_NAME;
173 }
174 *entryp1 = entry1;
175 *entryp2 = entry2;
176 return KERN_SUCCESS;
177}
178
179/*
180 * Routine: ipc_right_reverse
181 * Purpose:
182 * Translate (space, object) -> (name, entry).
183 * Only finds send/receive rights.
184 * Returns TRUE if an entry is found; if so,
185 * the object is locked and active.
186 * Conditions:
187 * The space must be locked (read or write) and active.
188 * Nothing else locked.
189 */
190
191boolean_t
192ipc_right_reverse(
0a7de745
A
193 ipc_space_t space,
194 ipc_object_t object,
195 mach_port_name_t *namep,
196 ipc_entry_t *entryp)
1c79356b
A
197{
198 ipc_port_t port;
199 mach_port_name_t name;
200 ipc_entry_t entry;
201
202 /* would switch on io_otype to handle multiple types of object */
203
316670eb 204 assert(is_active(space));
1c79356b
A
205 assert(io_otype(object) == IOT_PORT);
206
cb323159 207 port = ip_object_to_port(object);
1c79356b
A
208
209 ip_lock(port);
210 if (!ip_active(port)) {
211 ip_unlock(port);
212
213 return FALSE;
214 }
215
216 if (port->ip_receiver == space) {
217 name = port->ip_receiver_name;
218 assert(name != MACH_PORT_NULL);
219
220 entry = ipc_entry_lookup(space, name);
221
222 assert(entry != IE_NULL);
223 assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
cb323159 224 assert(port == ip_object_to_port(entry->ie_object));
1c79356b
A
225
226 *namep = name;
227 *entryp = entry;
228 return TRUE;
229 }
230
cb323159 231 if (ipc_hash_lookup(space, ip_to_object(port), namep, entryp)) {
1c79356b
A
232 assert((entry = *entryp) != IE_NULL);
233 assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND);
cb323159 234 assert(port == ip_object_to_port(entry->ie_object));
1c79356b
A
235
236 return TRUE;
237 }
238
239 ip_unlock(port);
240 return FALSE;
241}
242
243/*
244 * Routine: ipc_right_dnrequest
245 * Purpose:
246 * Make a dead-name request, returning the previously
247 * registered send-once right. If notify is IP_NULL,
248 * just cancels the previously registered request.
249 *
1c79356b
A
250 * Conditions:
251 * Nothing locked. May allocate memory.
252 * Only consumes/returns refs if successful.
253 * Returns:
254 * KERN_SUCCESS Made/canceled dead-name request.
255 * KERN_INVALID_TASK The space is dead.
256 * KERN_INVALID_NAME Name doesn't exist in space.
257 * KERN_INVALID_RIGHT Name doesn't denote port/dead rights.
258 * KERN_INVALID_ARGUMENT Name denotes dead name, but
259 * immediate is FALSE or notify is IP_NULL.
1c79356b
A
260 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
261 */
262
263kern_return_t
6d2010ae 264ipc_right_request_alloc(
0a7de745
A
265 ipc_space_t space,
266 mach_port_name_t name,
267 boolean_t immediate,
268 boolean_t send_possible,
269 ipc_port_t notify,
270 ipc_port_t *previousp)
1c79356b 271{
6d2010ae
A
272 ipc_port_request_index_t prev_request;
273 ipc_port_t previous = IP_NULL;
274 ipc_entry_t entry;
275 kern_return_t kr;
1c79356b 276
39236c6e
A
277#if IMPORTANCE_INHERITANCE
278 boolean_t needboost = FALSE;
279#endif /* IMPORTANCE_INHERITANCE */
280
1c79356b 281 for (;;) {
316670eb
A
282 ipc_port_t port = IP_NULL;
283
1c79356b 284 kr = ipc_right_lookup_write(space, name, &entry);
0a7de745 285 if (kr != KERN_SUCCESS) {
1c79356b 286 return kr;
0a7de745 287 }
6d2010ae 288
1c79356b 289 /* space is write-locked and active */
0a7de745 290
6d2010ae
A
291 prev_request = entry->ie_request;
292
293 /* if nothing to do or undo, we're done */
294 if (notify == IP_NULL && prev_request == IE_REQ_NONE) {
295 is_write_unlock(space);
296 *previousp = IP_NULL;
297 return KERN_SUCCESS;
298 }
299
300 /* see if the entry is of proper type for requests */
301 if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) {
302 ipc_port_request_index_t new_request;
1c79356b 303
cb323159 304 port = ip_object_to_port(entry->ie_object);
1c79356b
A
305 assert(port != IP_NULL);
306
0a7de745 307 if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b
A
308 /* port is locked and active */
309
6d2010ae 310 /* if no new request, just cancel previous */
1c79356b 311 if (notify == IP_NULL) {
0a7de745 312 if (prev_request != IE_REQ_NONE) {
6d2010ae 313 previous = ipc_port_request_cancel(port, name, prev_request);
0a7de745 314 }
1c79356b 315 ip_unlock(port);
6d2010ae 316 entry->ie_request = IE_REQ_NONE;
316670eb 317 ipc_entry_modified(space, name, entry);
1c79356b
A
318 is_write_unlock(space);
319 break;
320 }
321
322 /*
6d2010ae
A
323 * send-once rights, kernel objects, and non-full other queues
324 * fire immediately (if immediate specified).
1c79356b 325 */
6d2010ae
A
326 if (send_possible && immediate &&
327 ((entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE) ||
0a7de745
A
328 port->ip_receiver == ipc_space_kernel || !ip_full(port))) {
329 if (prev_request != IE_REQ_NONE) {
6d2010ae 330 previous = ipc_port_request_cancel(port, name, prev_request);
0a7de745 331 }
6d2010ae
A
332 ip_unlock(port);
333 entry->ie_request = IE_REQ_NONE;
316670eb 334 ipc_entry_modified(space, name, entry);
6d2010ae 335 is_write_unlock(space);
1c79356b 336
6d2010ae
A
337 ipc_notify_send_possible(notify, name);
338 break;
339 }
1c79356b 340
6d2010ae
A
341 /*
342 * If there is a previous request, free it. Any subsequent
343 * allocation cannot fail, thus assuring an atomic swap.
344 */
0a7de745 345 if (prev_request != IE_REQ_NONE) {
6d2010ae 346 previous = ipc_port_request_cancel(port, name, prev_request);
0a7de745 347 }
6d2010ae 348
39236c6e
A
349#if IMPORTANCE_INHERITANCE
350 kr = ipc_port_request_alloc(port, name, notify,
0a7de745
A
351 send_possible, immediate,
352 &new_request, &needboost);
39236c6e 353#else
6d2010ae 354 kr = ipc_port_request_alloc(port, name, notify,
0a7de745
A
355 send_possible, immediate,
356 &new_request);
39236c6e 357#endif /* IMPORTANCE_INHERITANCE */
1c79356b
A
358 if (kr != KERN_SUCCESS) {
359 assert(previous == IP_NULL);
360 is_write_unlock(space);
361
6d2010ae 362 kr = ipc_port_request_grow(port, ITS_SIZE_NONE);
1c79356b 363 /* port is unlocked */
6d2010ae 364
0a7de745 365 if (kr != KERN_SUCCESS) {
1c79356b 366 return kr;
0a7de745 367 }
1c79356b
A
368
369 continue;
370 }
371
39236c6e 372
6d2010ae 373 assert(new_request != IE_REQ_NONE);
6d2010ae 374 entry->ie_request = new_request;
316670eb 375 ipc_entry_modified(space, name, entry);
1c79356b 376 is_write_unlock(space);
39236c6e
A
377
378#if IMPORTANCE_INHERITANCE
379 if (needboost == TRUE) {
0a7de745 380 if (ipc_port_importance_delta(port, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE) {
39236c6e 381 ip_unlock(port);
0a7de745 382 }
39236c6e
A
383 } else
384#endif /* IMPORTANCE_INHERITANCE */
0a7de745 385 ip_unlock(port);
39236c6e 386
1c79356b 387 break;
1c79356b 388 }
6d2010ae 389 /* entry may have changed to dead-name by ipc_right_check() */
1c79356b
A
390 }
391
6d2010ae
A
392 /* treat send_possible requests as immediate w.r.t. dead-name */
393 if ((send_possible || immediate) && notify != IP_NULL &&
394 (entry->ie_bits & MACH_PORT_TYPE_DEAD_NAME)) {
395 mach_port_urefs_t urefs = IE_BITS_UREFS(entry->ie_bits);
1c79356b 396
1c79356b
A
397 assert(urefs > 0);
398
d190cdc3 399 /* leave urefs pegged to maximum if it overflowed */
0a7de745 400 if (urefs < MACH_PORT_UREFS_MAX) {
d190cdc3 401 (entry->ie_bits)++; /* increment urefs */
0a7de745 402 }
316670eb 403 ipc_entry_modified(space, name, entry);
d190cdc3 404
1c79356b
A
405 is_write_unlock(space);
406
0a7de745 407 if (port != IP_NULL) {
316670eb 408 ip_release(port);
0a7de745 409 }
316670eb 410
1c79356b
A
411 ipc_notify_dead_name(notify, name);
412 previous = IP_NULL;
413 break;
414 }
415
39037602
A
416 kr = (entry->ie_bits & MACH_PORT_TYPE_PORT_OR_DEAD) ?
417 KERN_INVALID_ARGUMENT : KERN_INVALID_RIGHT;
418
1c79356b 419 is_write_unlock(space);
316670eb 420
0a7de745 421 if (port != IP_NULL) {
316670eb 422 ip_release(port);
0a7de745 423 }
316670eb 424
39037602 425 return kr;
1c79356b
A
426 }
427
428 *previousp = previous;
429 return KERN_SUCCESS;
430}
431
432/*
6d2010ae 433 * Routine: ipc_right_request_cancel
1c79356b 434 * Purpose:
6d2010ae 435 * Cancel a notification request and return the send-once right.
1c79356b
A
436 * Afterwards, entry->ie_request == 0.
437 * Conditions:
438 * The space must be write-locked; the port must be locked.
439 * The port must be active; the space doesn't have to be.
440 */
441
442ipc_port_t
6d2010ae 443ipc_right_request_cancel(
0a7de745
A
444 __unused ipc_space_t space,
445 ipc_port_t port,
446 mach_port_name_t name,
447 ipc_entry_t entry)
1c79356b 448{
6d2010ae 449 ipc_port_t previous;
1c79356b 450
cb323159
A
451 require_ip_active(port);
452 assert(port == ip_object_to_port(entry->ie_object));
1c79356b 453
0a7de745 454 if (entry->ie_request == IE_REQ_NONE) {
6d2010ae 455 return IP_NULL;
0a7de745 456 }
1c79356b 457
6d2010ae
A
458 previous = ipc_port_request_cancel(port, name, entry->ie_request);
459 entry->ie_request = IE_REQ_NONE;
316670eb 460 ipc_entry_modified(space, name, entry);
6d2010ae 461 return previous;
1c79356b
A
462}
463
464/*
465 * Routine: ipc_right_inuse
466 * Purpose:
467 * Check if an entry is being used.
468 * Returns TRUE if it is.
469 * Conditions:
470 * The space is write-locked and active.
471 * It is unlocked if the entry is inuse.
472 */
473
474boolean_t
475ipc_right_inuse(
0a7de745
A
476 ipc_space_t space,
477 __unused mach_port_name_t name,
478 ipc_entry_t entry)
1c79356b
A
479{
480 if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_NONE) {
481 is_write_unlock(space);
482 return TRUE;
483 }
484 return FALSE;
485}
486
487/*
488 * Routine: ipc_right_check
489 * Purpose:
490 * Check if the port has died. If it has,
0a7de745
A
491 * and IPC_RIGHT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE is not
492 * passed and it is not a send once right then
1c79356b
A
493 * clean up the entry and return TRUE.
494 * Conditions:
495 * The space is write-locked; the port is not locked.
0a7de745 496 * If returns FALSE, the port is also locked.
316670eb
A
497 * Otherwise, entry is converted to a dead name.
498 *
499 * Caller is responsible for a reference to port if it
500 * had died (returns TRUE).
1c79356b
A
501 */
502
503boolean_t
504ipc_right_check(
0a7de745
A
505 ipc_space_t space,
506 ipc_port_t port,
507 mach_port_name_t name,
508 ipc_entry_t entry,
509 ipc_right_copyin_flags_t flags)
1c79356b
A
510{
511 ipc_entry_bits_t bits;
512
316670eb 513 assert(is_active(space));
cb323159 514 assert(port == ip_object_to_port(entry->ie_object));
1c79356b
A
515
516 ip_lock(port);
0a7de745
A
517 if (ip_active(port) ||
518 ((flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE) &&
519 entry->ie_request == IE_REQ_NONE &&
520 (entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE))) {
1c79356b 521 return FALSE;
0a7de745 522 }
1c79356b
A
523
524 /* this was either a pure send right or a send-once right */
525
526 bits = entry->ie_bits;
527 assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
528 assert(IE_BITS_UREFS(bits) > 0);
529
39236c6e 530 if (bits & MACH_PORT_TYPE_SEND) {
0a7de745 531 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
39236c6e
A
532 assert(IE_BITS_UREFS(bits) > 0);
533 assert(port->ip_srights > 0);
534 port->ip_srights--;
0a7de745
A
535 } else {
536 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
537 assert(IE_BITS_UREFS(bits) == 1);
39236c6e
A
538 assert(port->ip_sorights > 0);
539 port->ip_sorights--;
0a7de745 540 }
39236c6e 541 ip_unlock(port);
1c79356b 542
39236c6e
A
543 /*
544 * delete SEND rights from ipc hash.
545 */
1c79356b 546
39236c6e 547 if ((bits & MACH_PORT_TYPE_SEND) != 0) {
cb323159 548 ipc_hash_delete(space, ip_to_object(port), name, entry);
39236c6e 549 }
1c79356b 550
39236c6e 551 /* convert entry to dead name */
0a7de745
A
552 bits = (bits & ~IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME;
553
1c79356b
A
554 /*
555 * If there was a notification request outstanding on this
6d2010ae 556 * name, and the port went dead, that notification
39236c6e 557 * must already be on its way up from the port layer.
6d2010ae
A
558 *
559 * Add the reference that the notification carries. It
560 * is done here, and not in the notification delivery,
561 * because the latter doesn't have a space reference and
562 * trying to actually move a send-right reference would
563 * get short-circuited into a MACH_PORT_DEAD by IPC. Since
564 * all calls that deal with the right eventually come
565 * through here, it has the same result.
1c79356b 566 *
6d2010ae
A
567 * Once done, clear the request index so we only account
568 * for it once.
1c79356b 569 */
6d2010ae
A
570 if (entry->ie_request != IE_REQ_NONE) {
571 if (ipc_port_request_type(port, name, entry->ie_request) != 0) {
d190cdc3 572 /* if urefs are pegged due to overflow, leave them pegged */
0a7de745 573 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
d190cdc3 574 bits++; /* increment urefs */
0a7de745 575 }
6d2010ae 576 }
0a7de745 577 entry->ie_request = IE_REQ_NONE;
1c79356b
A
578 }
579 entry->ie_bits = bits;
580 entry->ie_object = IO_NULL;
316670eb 581 ipc_entry_modified(space, name, entry);
1c79356b
A
582 return TRUE;
583}
584
585/*
316670eb 586 * Routine: ipc_right_terminate
1c79356b 587 * Purpose:
316670eb 588 * Cleans up an entry in a terminated space.
1c79356b
A
589 * The entry isn't deallocated or removed
590 * from reverse hash tables.
591 * Conditions:
592 * The space is dead and unlocked.
593 */
594
595void
316670eb 596ipc_right_terminate(
0a7de745
A
597 ipc_space_t space,
598 mach_port_name_t name,
599 ipc_entry_t entry)
1c79356b
A
600{
601 ipc_entry_bits_t bits;
602 mach_port_type_t type;
603
604 bits = entry->ie_bits;
605 type = IE_BITS_TYPE(bits);
606
316670eb 607 assert(!is_active(space));
1c79356b
A
608
609 /*
610 * IE_BITS_COMPAT/ipc_right_dncancel doesn't have this
611 * problem, because we check that the port is active. If
612 * we didn't cancel IE_BITS_COMPAT, ipc_port_destroy
613 * would still work, but dead space refs would accumulate
614 * in ip_dnrequests. They would use up slots in
615 * ip_dnrequests and keep the spaces from being freed.
616 */
617
618 switch (type) {
0a7de745 619 case MACH_PORT_TYPE_DEAD_NAME:
6d2010ae 620 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
621 assert(entry->ie_object == IO_NULL);
622 break;
623
0a7de745 624 case MACH_PORT_TYPE_PORT_SET: {
cb323159 625 ipc_pset_t pset = ips_object_to_pset(entry->ie_object);
1c79356b 626
6d2010ae 627 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
628 assert(pset != IPS_NULL);
629
630 ips_lock(pset);
631 assert(ips_active(pset));
0a7de745 632 ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
1c79356b 633 break;
0a7de745 634 }
1c79356b 635
0a7de745
A
636 case MACH_PORT_TYPE_SEND:
637 case MACH_PORT_TYPE_RECEIVE:
638 case MACH_PORT_TYPE_SEND_RECEIVE:
639 case MACH_PORT_TYPE_SEND_ONCE: {
cb323159 640 ipc_port_t port = ip_object_to_port(entry->ie_object);
6d2010ae 641 ipc_port_t request;
1c79356b 642 ipc_port_t nsrequest = IP_NULL;
91447636 643 mach_port_mscount_t mscount = 0;
1c79356b
A
644
645 assert(port != IP_NULL);
646 ip_lock(port);
647
648 if (!ip_active(port)) {
316670eb 649 ip_unlock(port);
1c79356b 650 ip_release(port);
1c79356b
A
651 break;
652 }
653
0a7de745
A
654 request = ipc_right_request_cancel_macro(space, port,
655 name, entry);
1c79356b
A
656
657 if (type & MACH_PORT_TYPE_SEND) {
658 assert(port->ip_srights > 0);
659 if (--port->ip_srights == 0
660 ) {
661 nsrequest = port->ip_nsrequest;
662 if (nsrequest != IP_NULL) {
663 port->ip_nsrequest = IP_NULL;
664 mscount = port->ip_mscount;
665 }
666 }
667 }
668
669 if (type & MACH_PORT_TYPE_RECEIVE) {
670 assert(port->ip_receiver_name == name);
671 assert(port->ip_receiver == space);
672
39037602 673 ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
1c79356b
A
674 } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
675 assert(port->ip_sorights > 0);
cb323159 676 port->ip_reply_context = 0;
1c79356b
A
677 ip_unlock(port);
678
679 ipc_notify_send_once(port); /* consumes our ref */
680 } else {
681 assert(port->ip_receiver != space);
682
316670eb 683 ip_unlock(port);
0a7de745 684 ip_release(port);
1c79356b
A
685 }
686
0a7de745 687 if (nsrequest != IP_NULL) {
1c79356b 688 ipc_notify_no_senders(nsrequest, mscount);
0a7de745 689 }
1c79356b 690
0a7de745 691 if (request != IP_NULL) {
6d2010ae 692 ipc_notify_port_deleted(request, name);
0a7de745 693 }
1c79356b 694 break;
0a7de745 695 }
1c79356b 696
0a7de745 697 default:
316670eb 698 panic("ipc_right_terminate: strange type - 0x%x", type);
1c79356b
A
699 }
700}
701
702/*
703 * Routine: ipc_right_destroy
704 * Purpose:
705 * Destroys an entry in a space.
706 * Conditions:
316670eb 707 * The space is write-locked (returns unlocked).
1c79356b
A
708 * The space must be active.
709 * Returns:
710 * KERN_SUCCESS The entry was destroyed.
711 */
712
713kern_return_t
714ipc_right_destroy(
0a7de745
A
715 ipc_space_t space,
716 mach_port_name_t name,
717 ipc_entry_t entry,
718 boolean_t check_guard,
719 uint64_t guard)
1c79356b
A
720{
721 ipc_entry_bits_t bits;
722 mach_port_type_t type;
723
724 bits = entry->ie_bits;
725 entry->ie_bits &= ~IE_BITS_TYPE_MASK;
726 type = IE_BITS_TYPE(bits);
727
316670eb 728 assert(is_active(space));
1c79356b
A
729
730 switch (type) {
0a7de745 731 case MACH_PORT_TYPE_DEAD_NAME:
6d2010ae 732 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
733 assert(entry->ie_object == IO_NULL);
734
735 ipc_entry_dealloc(space, name, entry);
316670eb 736 is_write_unlock(space);
1c79356b
A
737 break;
738
0a7de745 739 case MACH_PORT_TYPE_PORT_SET: {
cb323159 740 ipc_pset_t pset = ips_object_to_pset(entry->ie_object);
1c79356b 741
6d2010ae 742 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
743 assert(pset != IPS_NULL);
744
745 entry->ie_object = IO_NULL;
1c79356b
A
746 ipc_entry_dealloc(space, name, entry);
747
748 ips_lock(pset);
316670eb 749 is_write_unlock(space);
1c79356b 750
316670eb 751 assert(ips_active(pset));
0a7de745 752 ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
1c79356b 753 break;
0a7de745 754 }
1c79356b 755
0a7de745
A
756 case MACH_PORT_TYPE_SEND:
757 case MACH_PORT_TYPE_RECEIVE:
758 case MACH_PORT_TYPE_SEND_RECEIVE:
759 case MACH_PORT_TYPE_SEND_ONCE: {
cb323159 760 ipc_port_t port = ip_object_to_port(entry->ie_object);
1c79356b 761 ipc_port_t nsrequest = IP_NULL;
91447636 762 mach_port_mscount_t mscount = 0;
6d2010ae 763 ipc_port_t request;
1c79356b
A
764
765 assert(port != IP_NULL);
766
0a7de745 767 if (type == MACH_PORT_TYPE_SEND) {
cb323159 768 ipc_hash_delete(space, ip_to_object(port), name, entry);
0a7de745 769 }
1c79356b
A
770
771 ip_lock(port);
772
773 if (!ip_active(port)) {
774 assert((type & MACH_PORT_TYPE_RECEIVE) == 0);
316670eb 775 ip_unlock(port);
6d2010ae 776 entry->ie_request = IE_REQ_NONE;
1c79356b
A
777 entry->ie_object = IO_NULL;
778 ipc_entry_dealloc(space, name, entry);
316670eb
A
779 is_write_unlock(space);
780 ip_release(port);
1c79356b
A
781 break;
782 }
783
39236c6e
A
784 /* For receive rights, check for guarding */
785 if ((type & MACH_PORT_TYPE_RECEIVE) &&
786 (check_guard) && (port->ip_guarded) &&
787 (guard != port->ip_context)) {
788 /* Guard Violation */
789 uint64_t portguard = port->ip_context;
790 ip_unlock(port);
791 is_write_unlock(space);
792 /* Raise mach port guard exception */
793 mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
0a7de745 794 return KERN_INVALID_RIGHT;
39236c6e
A
795 }
796
797
6d2010ae 798 request = ipc_right_request_cancel_macro(space, port, name, entry);
1c79356b
A
799
800 entry->ie_object = IO_NULL;
801 ipc_entry_dealloc(space, name, entry);
316670eb 802 is_write_unlock(space);
1c79356b
A
803
804 if (type & MACH_PORT_TYPE_SEND) {
805 assert(port->ip_srights > 0);
806 if (--port->ip_srights == 0) {
807 nsrequest = port->ip_nsrequest;
808 if (nsrequest != IP_NULL) {
809 port->ip_nsrequest = IP_NULL;
810 mscount = port->ip_mscount;
811 }
812 }
813 }
814
815 if (type & MACH_PORT_TYPE_RECEIVE) {
cb323159 816 require_ip_active(port);
1c79356b
A
817 assert(port->ip_receiver == space);
818
39037602 819 ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
1c79356b
A
820 } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
821 assert(port->ip_sorights > 0);
cb323159 822 port->ip_reply_context = 0;
1c79356b
A
823 ip_unlock(port);
824
825 ipc_notify_send_once(port); /* consumes our ref */
826 } else {
827 assert(port->ip_receiver != space);
828
1c79356b 829 ip_unlock(port);
316670eb 830 ip_release(port);
1c79356b
A
831 }
832
0a7de745 833 if (nsrequest != IP_NULL) {
1c79356b 834 ipc_notify_no_senders(nsrequest, mscount);
0a7de745 835 }
1c79356b 836
0a7de745 837 if (request != IP_NULL) {
6d2010ae 838 ipc_notify_port_deleted(request, name);
0a7de745 839 }
39236c6e
A
840
841
1c79356b 842 break;
0a7de745 843 }
1c79356b 844
0a7de745 845 default:
1c79356b
A
846 panic("ipc_right_destroy: strange type");
847 }
848
849 return KERN_SUCCESS;
850}
851
852/*
853 * Routine: ipc_right_dealloc
854 * Purpose:
5ba3f43e 855 * Releases a send/send-once/dead-name/port_set user ref.
1c79356b
A
856 * Like ipc_right_delta with a delta of -1,
857 * but looks at the entry to determine the right.
858 * Conditions:
859 * The space is write-locked, and is unlocked upon return.
860 * The space must be active.
861 * Returns:
862 * KERN_SUCCESS A user ref was released.
863 * KERN_INVALID_RIGHT Entry has wrong type.
864 */
865
866kern_return_t
867ipc_right_dealloc(
0a7de745
A
868 ipc_space_t space,
869 mach_port_name_t name,
870 ipc_entry_t entry)
1c79356b 871{
316670eb 872 ipc_port_t port = IP_NULL;
1c79356b
A
873 ipc_entry_bits_t bits;
874 mach_port_type_t type;
875
876 bits = entry->ie_bits;
877 type = IE_BITS_TYPE(bits);
878
879
316670eb 880 assert(is_active(space));
1c79356b
A
881
882 switch (type) {
0a7de745 883 case MACH_PORT_TYPE_PORT_SET: {
5ba3f43e
A
884 ipc_pset_t pset;
885
886 assert(IE_BITS_UREFS(bits) == 0);
887 assert(entry->ie_request == IE_REQ_NONE);
888
cb323159 889 pset = ips_object_to_pset(entry->ie_object);
5ba3f43e
A
890 assert(pset != IPS_NULL);
891
892 entry->ie_object = IO_NULL;
893 ipc_entry_dealloc(space, name, entry);
894
895 ips_lock(pset);
896 assert(ips_active(pset));
897 is_write_unlock(space);
898
0a7de745 899 ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
5ba3f43e 900 break;
0a7de745 901 }
5ba3f43e 902
0a7de745
A
903 case MACH_PORT_TYPE_DEAD_NAME: {
904dead_name:
1c79356b
A
905
906 assert(IE_BITS_UREFS(bits) > 0);
6d2010ae 907 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
908 assert(entry->ie_object == IO_NULL);
909
910 if (IE_BITS_UREFS(bits) == 1) {
911 ipc_entry_dealloc(space, name, entry);
316670eb 912 } else {
d190cdc3 913 /* if urefs are pegged due to overflow, leave them pegged */
0a7de745
A
914 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
915 entry->ie_bits = bits - 1; /* decrement urefs */
916 }
316670eb
A
917 ipc_entry_modified(space, name, entry);
918 }
1c79356b 919 is_write_unlock(space);
316670eb
A
920
921 /* release any port that got converted to dead name below */
0a7de745 922 if (port != IP_NULL) {
316670eb 923 ip_release(port);
0a7de745 924 }
1c79356b 925 break;
0a7de745 926 }
1c79356b 927
0a7de745 928 case MACH_PORT_TYPE_SEND_ONCE: {
316670eb 929 ipc_port_t request;
1c79356b
A
930
931 assert(IE_BITS_UREFS(bits) == 1);
932
cb323159 933 port = ip_object_to_port(entry->ie_object);
1c79356b
A
934 assert(port != IP_NULL);
935
0a7de745 936 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b
A
937 bits = entry->ie_bits;
938 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
316670eb 939 goto dead_name; /* it will release port */
1c79356b
A
940 }
941 /* port is locked and active */
942
943 assert(port->ip_sorights > 0);
944
cb323159
A
945 /*
946 * clear any reply context:
947 * no one will be sending the response b/c we are destroying
948 * the single, outstanding send once right.
949 */
950 port->ip_reply_context = 0;
951
6d2010ae 952 request = ipc_right_request_cancel_macro(space, port, name, entry);
1c79356b
A
953 ip_unlock(port);
954
955 entry->ie_object = IO_NULL;
956 ipc_entry_dealloc(space, name, entry);
957
958 is_write_unlock(space);
959
960 ipc_notify_send_once(port);
961
0a7de745 962 if (request != IP_NULL) {
6d2010ae 963 ipc_notify_port_deleted(request, name);
0a7de745 964 }
1c79356b 965 break;
0a7de745 966 }
1c79356b 967
0a7de745 968 case MACH_PORT_TYPE_SEND: {
6d2010ae 969 ipc_port_t request = IP_NULL;
1c79356b 970 ipc_port_t nsrequest = IP_NULL;
91447636 971 mach_port_mscount_t mscount = 0;
1c79356b
A
972
973
974 assert(IE_BITS_UREFS(bits) > 0);
975
cb323159 976 port = ip_object_to_port(entry->ie_object);
1c79356b
A
977 assert(port != IP_NULL);
978
0a7de745 979 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b
A
980 bits = entry->ie_bits;
981 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
316670eb 982 goto dead_name; /* it will release port */
1c79356b
A
983 }
984 /* port is locked and active */
985
986 assert(port->ip_srights > 0);
987
988 if (IE_BITS_UREFS(bits) == 1) {
989 if (--port->ip_srights == 0) {
990 nsrequest = port->ip_nsrequest;
991 if (nsrequest != IP_NULL) {
992 port->ip_nsrequest = IP_NULL;
993 mscount = port->ip_mscount;
994 }
995 }
996
6d2010ae 997 request = ipc_right_request_cancel_macro(space, port,
0a7de745 998 name, entry);
cb323159 999 ipc_hash_delete(space, ip_to_object(port), name, entry);
1c79356b 1000
316670eb 1001 ip_unlock(port);
1c79356b
A
1002 entry->ie_object = IO_NULL;
1003 ipc_entry_dealloc(space, name, entry);
316670eb
A
1004 is_write_unlock(space);
1005 ip_release(port);
316670eb 1006 } else {
d190cdc3
A
1007 ip_unlock(port);
1008 /* if urefs are pegged due to overflow, leave them pegged */
0a7de745
A
1009 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
1010 entry->ie_bits = bits - 1; /* decrement urefs */
1011 }
316670eb
A
1012 ipc_entry_modified(space, name, entry);
1013 is_write_unlock(space);
1014 }
1c79356b 1015
0a7de745 1016 if (nsrequest != IP_NULL) {
1c79356b 1017 ipc_notify_no_senders(nsrequest, mscount);
0a7de745 1018 }
1c79356b 1019
0a7de745 1020 if (request != IP_NULL) {
6d2010ae 1021 ipc_notify_port_deleted(request, name);
0a7de745 1022 }
1c79356b 1023 break;
0a7de745 1024 }
1c79356b 1025
0a7de745 1026 case MACH_PORT_TYPE_SEND_RECEIVE: {
1c79356b 1027 ipc_port_t nsrequest = IP_NULL;
91447636 1028 mach_port_mscount_t mscount = 0;
1c79356b
A
1029
1030 assert(IE_BITS_UREFS(bits) > 0);
1031
cb323159 1032 port = ip_object_to_port(entry->ie_object);
1c79356b
A
1033 assert(port != IP_NULL);
1034
1035 ip_lock(port);
cb323159 1036 require_ip_active(port);
1c79356b
A
1037 assert(port->ip_receiver_name == name);
1038 assert(port->ip_receiver == space);
1039 assert(port->ip_srights > 0);
1040
1041 if (IE_BITS_UREFS(bits) == 1) {
1042 if (--port->ip_srights == 0) {
1043 nsrequest = port->ip_nsrequest;
1044 if (nsrequest != IP_NULL) {
1045 port->ip_nsrequest = IP_NULL;
1046 mscount = port->ip_mscount;
1047 }
1048 }
1049
0a7de745
A
1050 entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK |
1051 MACH_PORT_TYPE_SEND);
d190cdc3
A
1052 } else {
1053 /* if urefs are pegged due to overflow, leave them pegged */
1054 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
0a7de745 1055 entry->ie_bits = bits - 1; /* decrement urefs */
d190cdc3
A
1056 }
1057 }
1c79356b 1058 ip_unlock(port);
316670eb
A
1059
1060 ipc_entry_modified(space, name, entry);
1c79356b
A
1061 is_write_unlock(space);
1062
0a7de745 1063 if (nsrequest != IP_NULL) {
1c79356b 1064 ipc_notify_no_senders(nsrequest, mscount);
0a7de745 1065 }
1c79356b 1066 break;
0a7de745 1067 }
1c79356b 1068
0a7de745 1069 default:
1c79356b 1070 is_write_unlock(space);
d9a64523 1071 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1c79356b
A
1072 return KERN_INVALID_RIGHT;
1073 }
1074
1075 return KERN_SUCCESS;
1076}
1077
1078/*
1079 * Routine: ipc_right_delta
1080 * Purpose:
1081 * Modifies the user-reference count for a right.
1082 * May deallocate the right, if the count goes to zero.
1083 * Conditions:
1084 * The space is write-locked, and is unlocked upon return.
1085 * The space must be active.
1086 * Returns:
1087 * KERN_SUCCESS Count was modified.
1088 * KERN_INVALID_RIGHT Entry has wrong type.
1089 * KERN_INVALID_VALUE Bad delta for the right.
1c79356b
A
1090 */
1091
1092kern_return_t
1093ipc_right_delta(
0a7de745
A
1094 ipc_space_t space,
1095 mach_port_name_t name,
1096 ipc_entry_t entry,
1097 mach_port_right_t right,
1098 mach_port_delta_t delta)
1c79356b 1099{
316670eb 1100 ipc_port_t port = IP_NULL;
1c79356b 1101 ipc_entry_bits_t bits;
39236c6e 1102
1c79356b
A
1103 bits = entry->ie_bits;
1104
1c79356b
A
1105/*
1106 * The following is used (for case MACH_PORT_RIGHT_DEAD_NAME) in the
1107 * switch below. It is used to keep track of those cases (in DIPC)
1108 * where we have postponed the dropping of a port reference. Since
1109 * the dropping of the reference could cause the port to disappear
1110 * we postpone doing so when we are holding the space lock.
1111 */
1112
316670eb 1113 assert(is_active(space));
1c79356b
A
1114 assert(right < MACH_PORT_RIGHT_NUMBER);
1115
1116 /* Rights-specific restrictions and operations. */
1117
1118 switch (right) {
0a7de745 1119 case MACH_PORT_RIGHT_PORT_SET: {
1c79356b
A
1120 ipc_pset_t pset;
1121
d9a64523
A
1122 if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) {
1123 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1c79356b 1124 goto invalid_right;
d9a64523 1125 }
1c79356b
A
1126
1127 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET);
1128 assert(IE_BITS_UREFS(bits) == 0);
6d2010ae 1129 assert(entry->ie_request == IE_REQ_NONE);
1c79356b 1130
0a7de745 1131 if (delta == 0) {
1c79356b 1132 goto success;
0a7de745 1133 }
1c79356b 1134
0a7de745 1135 if (delta != -1) {
1c79356b 1136 goto invalid_value;
0a7de745 1137 }
1c79356b 1138
cb323159 1139 pset = ips_object_to_pset(entry->ie_object);
1c79356b
A
1140 assert(pset != IPS_NULL);
1141
1c79356b
A
1142 entry->ie_object = IO_NULL;
1143 ipc_entry_dealloc(space, name, entry);
1144
1c79356b
A
1145 ips_lock(pset);
1146 assert(ips_active(pset));
1147 is_write_unlock(space);
1148
0a7de745 1149 ipc_pset_destroy(space, pset); /* consumes ref, unlocks */
1c79356b 1150 break;
0a7de745 1151 }
1c79356b 1152
0a7de745 1153 case MACH_PORT_RIGHT_RECEIVE: {
6d2010ae 1154 ipc_port_t request = IP_NULL;
1c79356b 1155
d9a64523 1156 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
cb323159
A
1157 if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
1158 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1159 }
1c79356b 1160 goto invalid_right;
d9a64523 1161 }
1c79356b 1162
0a7de745 1163 if (delta == 0) {
1c79356b 1164 goto success;
0a7de745 1165 }
1c79356b 1166
0a7de745 1167 if (delta != -1) {
1c79356b 1168 goto invalid_value;
0a7de745 1169 }
1c79356b 1170
cb323159 1171 port = ip_object_to_port(entry->ie_object);
1c79356b
A
1172 assert(port != IP_NULL);
1173
1174 /*
1175 * The port lock is needed for ipc_right_dncancel;
1176 * otherwise, we wouldn't have to take the lock
1177 * until just before dropping the space lock.
1178 */
1179
1180 ip_lock(port);
cb323159 1181 require_ip_active(port);
1c79356b
A
1182 assert(port->ip_receiver_name == name);
1183 assert(port->ip_receiver == space);
0a7de745 1184
39236c6e 1185 /* Mach Port Guard Checking */
0a7de745 1186 if (port->ip_guarded) {
39236c6e
A
1187 uint64_t portguard = port->ip_context;
1188 ip_unlock(port);
1189 is_write_unlock(space);
1190 /* Raise mach port guard exception */
1191 mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_MOD_REFS);
1192 goto guard_failure;
1193 }
0a7de745 1194
1c79356b
A
1195 if (bits & MACH_PORT_TYPE_SEND) {
1196 assert(IE_BITS_TYPE(bits) ==
0a7de745 1197 MACH_PORT_TYPE_SEND_RECEIVE);
1c79356b 1198 assert(IE_BITS_UREFS(bits) > 0);
1c79356b
A
1199 assert(port->ip_srights > 0);
1200
6d2010ae
A
1201 if (port->ip_pdrequest != NULL) {
1202 /*
1203 * Since another task has requested a
1204 * destroy notification for this port, it
1205 * isn't actually being destroyed - the receive
1206 * right is just being moved to another task.
1207 * Since we still have one or more send rights,
1208 * we need to record the loss of the receive
1209 * right and enter the remaining send right
1210 * into the hash table.
1211 */
316670eb 1212 ipc_entry_modified(space, name, entry);
6d2010ae 1213 entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
cb323159
A
1214 entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
1215 ipc_hash_insert(space, ip_to_object(port),
6d2010ae
A
1216 name, entry);
1217 ip_reference(port);
1218 } else {
1219 /*
1220 * The remaining send right turns into a
1221 * dead name. Notice we don't decrement
1222 * ip_srights, generate a no-senders notif,
1223 * or use ipc_right_dncancel, because the
1224 * port is destroyed "first".
1225 */
1226 bits &= ~IE_BITS_TYPE_MASK;
cb323159 1227 bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE);
6d2010ae
A
1228 if (entry->ie_request) {
1229 entry->ie_request = IE_REQ_NONE;
d190cdc3 1230 /* if urefs are pegged due to overflow, leave them pegged */
0a7de745 1231 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
d190cdc3 1232 bits++; /* increment urefs */
0a7de745 1233 }
6d2010ae
A
1234 }
1235 entry->ie_bits = bits;
1236 entry->ie_object = IO_NULL;
316670eb 1237 ipc_entry_modified(space, name, entry);
1c79356b 1238 }
1c79356b
A
1239 } else {
1240 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
1241 assert(IE_BITS_UREFS(bits) == 0);
1242
6d2010ae 1243 request = ipc_right_request_cancel_macro(space, port,
0a7de745 1244 name, entry);
1c79356b
A
1245 entry->ie_object = IO_NULL;
1246 ipc_entry_dealloc(space, name, entry);
1247 }
1248 is_write_unlock(space);
1249
0a7de745 1250 ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
39236c6e 1251
0a7de745 1252 if (request != IP_NULL) {
6d2010ae 1253 ipc_notify_port_deleted(request, name);
0a7de745 1254 }
1c79356b 1255 break;
0a7de745 1256 }
1c79356b 1257
0a7de745 1258 case MACH_PORT_RIGHT_SEND_ONCE: {
316670eb 1259 ipc_port_t request;
1c79356b 1260
0a7de745 1261 if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
1c79356b 1262 goto invalid_right;
0a7de745 1263 }
1c79356b
A
1264
1265 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
1266 assert(IE_BITS_UREFS(bits) == 1);
1267
cb323159 1268 port = ip_object_to_port(entry->ie_object);
1c79356b
A
1269 assert(port != IP_NULL);
1270
0a7de745 1271 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b 1272 assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE));
d9a64523 1273 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1c79356b
A
1274 goto invalid_right;
1275 }
1276 /* port is locked and active */
1277
1278 assert(port->ip_sorights > 0);
1279
1280 if ((delta > 0) || (delta < -1)) {
1281 ip_unlock(port);
1282 goto invalid_value;
1283 }
1284
1285 if (delta == 0) {
1286 ip_unlock(port);
1287 goto success;
1288 }
1289
cb323159
A
1290 /*
1291 * clear any reply context:
1292 * no one will be sending the response b/c we are destroying
1293 * the single, outstanding send once right.
1294 */
1295 port->ip_reply_context = 0;
1296
6d2010ae 1297 request = ipc_right_request_cancel_macro(space, port, name, entry);
1c79356b
A
1298 ip_unlock(port);
1299
1300 entry->ie_object = IO_NULL;
1301 ipc_entry_dealloc(space, name, entry);
1302
1303 is_write_unlock(space);
1304
1305 ipc_notify_send_once(port);
1306
0a7de745 1307 if (request != IP_NULL) {
6d2010ae 1308 ipc_notify_port_deleted(request, name);
0a7de745 1309 }
1c79356b 1310 break;
0a7de745 1311 }
1c79356b 1312
0a7de745 1313 case MACH_PORT_RIGHT_DEAD_NAME: {
39236c6e 1314 ipc_port_t relport = IP_NULL;
1c79356b
A
1315 mach_port_urefs_t urefs;
1316
1317 if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
cb323159 1318 port = ip_object_to_port(entry->ie_object);
1c79356b
A
1319 assert(port != IP_NULL);
1320
0a7de745 1321 if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b
A
1322 /* port is locked and active */
1323 ip_unlock(port);
316670eb 1324 port = IP_NULL;
d9a64523 1325 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1c79356b
A
1326 goto invalid_right;
1327 }
1328 bits = entry->ie_bits;
39236c6e
A
1329 relport = port;
1330 port = IP_NULL;
d190cdc3 1331 } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) {
d9a64523 1332 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1c79356b 1333 goto invalid_right;
d190cdc3 1334 }
1c79356b
A
1335
1336 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
1337 assert(IE_BITS_UREFS(bits) > 0);
1338 assert(entry->ie_object == IO_NULL);
6d2010ae 1339 assert(entry->ie_request == IE_REQ_NONE);
1c79356b 1340
0a7de745 1341 if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) ||
d190cdc3 1342 delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
1c79356b 1343 goto invalid_value;
d190cdc3
A
1344 }
1345
1346 urefs = IE_BITS_UREFS(bits);
1347
1348 if (urefs == MACH_PORT_UREFS_MAX) {
1349 /*
1350 * urefs are pegged due to an overflow
1351 * only a delta removing all refs at once can change it
1352 */
1353
0a7de745 1354 if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
d190cdc3 1355 delta = 0;
0a7de745 1356 }
d190cdc3 1357 } else {
0a7de745 1358 if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
d190cdc3 1359 goto invalid_value;
0a7de745 1360 }
d190cdc3
A
1361 if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) {
1362 /* leave urefs pegged to maximum if it overflowed */
1363 delta = MACH_PORT_UREFS_MAX - urefs;
1364 }
1365 }
1c79356b
A
1366
1367 if ((urefs + delta) == 0) {
1368 ipc_entry_dealloc(space, name, entry);
d190cdc3 1369 } else if (delta != 0) {
1c79356b 1370 entry->ie_bits = bits + delta;
316670eb
A
1371 ipc_entry_modified(space, name, entry);
1372 }
d190cdc3 1373
1c79356b
A
1374 is_write_unlock(space);
1375
0a7de745 1376 if (relport != IP_NULL) {
39236c6e 1377 ip_release(relport);
0a7de745 1378 }
39236c6e 1379
1c79356b 1380 break;
0a7de745 1381 }
1c79356b 1382
0a7de745 1383 case MACH_PORT_RIGHT_SEND: {
1c79356b 1384 mach_port_urefs_t urefs;
6d2010ae 1385 ipc_port_t request = IP_NULL;
1c79356b 1386 ipc_port_t nsrequest = IP_NULL;
813fb2f6 1387 ipc_port_t port_to_release = IP_NULL;
91447636 1388 mach_port_mscount_t mscount = 0;
1c79356b 1389
d9a64523
A
1390 if ((bits & MACH_PORT_TYPE_SEND) == 0) {
1391 /* invalid right exception only when not live/dead confusion */
cb323159
A
1392 if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0
1393#if !defined(AE_MAKESENDRIGHT_FIXED)
1394 /*
1395 * AE tries to add single send right without knowing if it already owns one.
1396 * But if it doesn't, it should own the receive right and delta should be 1.
1397 */
1398 && (((bits & MACH_PORT_TYPE_RECEIVE) == 0) || (delta != 1))
1399#endif
1400 ) {
d9a64523
A
1401 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1402 }
1c79356b 1403 goto invalid_right;
d9a64523 1404 }
1c79356b 1405
d190cdc3 1406 /* maximum urefs for send is MACH_PORT_UREFS_MAX */
1c79356b 1407
cb323159 1408 port = ip_object_to_port(entry->ie_object);
1c79356b
A
1409 assert(port != IP_NULL);
1410
0a7de745 1411 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b
A
1412 assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0);
1413 goto invalid_right;
1414 }
1415 /* port is locked and active */
1416
1417 assert(port->ip_srights > 0);
1418
0a7de745 1419 if (delta > ((mach_port_delta_t)MACH_PORT_UREFS_MAX) ||
d190cdc3 1420 delta < (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
1c79356b
A
1421 ip_unlock(port);
1422 goto invalid_value;
1423 }
d190cdc3
A
1424
1425 urefs = IE_BITS_UREFS(bits);
1426
1427 if (urefs == MACH_PORT_UREFS_MAX) {
1428 /*
1429 * urefs are pegged due to an overflow
1430 * only a delta removing all refs at once can change it
1431 */
1432
0a7de745 1433 if (delta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
d190cdc3 1434 delta = 0;
0a7de745 1435 }
d190cdc3
A
1436 } else {
1437 if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) {
1438 ip_unlock(port);
1439 goto invalid_value;
1440 }
1441 if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) {
1442 /* leave urefs pegged to maximum if it overflowed */
1443 delta = MACH_PORT_UREFS_MAX - urefs;
1444 }
1c79356b
A
1445 }
1446
1447 if ((urefs + delta) == 0) {
1448 if (--port->ip_srights == 0) {
1449 nsrequest = port->ip_nsrequest;
1450 if (nsrequest != IP_NULL) {
1451 port->ip_nsrequest = IP_NULL;
1452 mscount = port->ip_mscount;
1453 }
1454 }
1455
1456 if (bits & MACH_PORT_TYPE_RECEIVE) {
1457 assert(port->ip_receiver_name == name);
1458 assert(port->ip_receiver == space);
d190cdc3 1459 ip_unlock(port);
1c79356b 1460 assert(IE_BITS_TYPE(bits) ==
0a7de745 1461 MACH_PORT_TYPE_SEND_RECEIVE);
1c79356b 1462
0a7de745
A
1463 entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK |
1464 MACH_PORT_TYPE_SEND);
316670eb 1465 ipc_entry_modified(space, name, entry);
1c79356b
A
1466 } else {
1467 assert(IE_BITS_TYPE(bits) ==
0a7de745 1468 MACH_PORT_TYPE_SEND);
1c79356b 1469
6d2010ae 1470 request = ipc_right_request_cancel_macro(space, port,
0a7de745 1471 name, entry);
cb323159 1472 ipc_hash_delete(space, ip_to_object(port),
0a7de745 1473 name, entry);
1c79356b 1474
316670eb 1475 ip_unlock(port);
813fb2f6 1476 port_to_release = port;
1c79356b
A
1477
1478 entry->ie_object = IO_NULL;
1479 ipc_entry_dealloc(space, name, entry);
1480 }
d190cdc3 1481 } else if (delta != 0) {
316670eb 1482 ip_unlock(port);
1c79356b 1483 entry->ie_bits = bits + delta;
316670eb 1484 ipc_entry_modified(space, name, entry);
d190cdc3
A
1485 } else {
1486 ip_unlock(port);
316670eb 1487 }
1c79356b 1488
1c79356b
A
1489 is_write_unlock(space);
1490
0a7de745 1491 if (port_to_release != IP_NULL) {
813fb2f6 1492 ip_release(port_to_release);
0a7de745 1493 }
813fb2f6 1494
0a7de745 1495 if (nsrequest != IP_NULL) {
1c79356b 1496 ipc_notify_no_senders(nsrequest, mscount);
0a7de745 1497 }
1c79356b 1498
0a7de745 1499 if (request != IP_NULL) {
6d2010ae 1500 ipc_notify_port_deleted(request, name);
0a7de745 1501 }
1c79356b 1502 break;
0a7de745 1503 }
1c79356b 1504
0a7de745 1505 case MACH_PORT_RIGHT_LABELH:
5ba3f43e
A
1506 goto invalid_right;
1507
0a7de745 1508 default:
5ba3f43e 1509 panic("ipc_right_delta: strange right %d for 0x%x (%p) in space:%p",
0a7de745 1510 right, name, (void *)entry, (void *)space);
1c79356b
A
1511 }
1512
1513 return KERN_SUCCESS;
1514
0a7de745 1515success:
1c79356b
A
1516 is_write_unlock(space);
1517 return KERN_SUCCESS;
1518
0a7de745 1519invalid_right:
1c79356b 1520 is_write_unlock(space);
0a7de745 1521 if (port != IP_NULL) {
316670eb 1522 ip_release(port);
0a7de745 1523 }
1c79356b
A
1524 return KERN_INVALID_RIGHT;
1525
0a7de745 1526invalid_value:
1c79356b 1527 is_write_unlock(space);
d9a64523 1528 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE);
1c79356b
A
1529 return KERN_INVALID_VALUE;
1530
0a7de745 1531guard_failure:
d190cdc3 1532 return KERN_INVALID_RIGHT;
1c79356b
A
1533}
1534
39236c6e
A
1535/*
1536 * Routine: ipc_right_destruct
1537 * Purpose:
0a7de745 1538 * Deallocates the receive right and modifies the
39236c6e
A
1539 * user-reference count for the send rights as requested.
1540 * Conditions:
1541 * The space is write-locked, and is unlocked upon return.
1542 * The space must be active.
1543 * Returns:
1544 * KERN_SUCCESS Count was modified.
1545 * KERN_INVALID_RIGHT Entry has wrong type.
1546 * KERN_INVALID_VALUE Bad delta for the right.
1547 */
1548
1549kern_return_t
1550ipc_right_destruct(
0a7de745
A
1551 ipc_space_t space,
1552 mach_port_name_t name,
1553 ipc_entry_t entry,
1554 mach_port_delta_t srdelta,
1555 uint64_t guard)
39236c6e
A
1556{
1557 ipc_port_t port = IP_NULL;
1558 ipc_entry_bits_t bits;
1559
39236c6e
A
1560 mach_port_urefs_t urefs;
1561 ipc_port_t request = IP_NULL;
1562 ipc_port_t nsrequest = IP_NULL;
1563 mach_port_mscount_t mscount = 0;
1564
1565 bits = entry->ie_bits;
d9a64523 1566
39236c6e
A
1567 assert(is_active(space));
1568
cb323159
A
1569 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
1570 is_write_unlock(space);
1571
1572 /* No exception if we used to have receive and held entry since */
1573 if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
1574 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
1575 }
1576 return KERN_INVALID_RIGHT;
1577 }
1578
1579 if (srdelta && (bits & MACH_PORT_TYPE_SEND) == 0) {
39236c6e 1580 is_write_unlock(space);
d9a64523 1581 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
39236c6e
A
1582 return KERN_INVALID_RIGHT;
1583 }
1584
0a7de745 1585 if (srdelta > 0) {
39236c6e 1586 goto invalid_value;
0a7de745 1587 }
39236c6e 1588
cb323159 1589 port = ip_object_to_port(entry->ie_object);
39236c6e 1590 assert(port != IP_NULL);
d190cdc3 1591
39236c6e 1592 ip_lock(port);
cb323159 1593 require_ip_active(port);
39236c6e
A
1594 assert(port->ip_receiver_name == name);
1595 assert(port->ip_receiver == space);
1596
1597 /* Mach Port Guard Checking */
0a7de745 1598 if (port->ip_guarded && (guard != port->ip_context)) {
39236c6e
A
1599 uint64_t portguard = port->ip_context;
1600 ip_unlock(port);
1601 is_write_unlock(space);
1602 mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_DESTROY);
1603 return KERN_INVALID_ARGUMENT;
1604 }
1605
1606 /*
1607 * First reduce the send rights as requested and
1608 * adjust the entry->ie_bits accordingly. The
1609 * ipc_entry_modified() call is made once the receive
1610 * right is destroyed too.
1611 */
1612
1613 if (srdelta) {
39236c6e
A
1614 assert(port->ip_srights > 0);
1615
1616 urefs = IE_BITS_UREFS(bits);
d190cdc3 1617
39236c6e
A
1618 /*
1619 * Since we made sure that srdelta is negative,
1620 * the check for urefs overflow is not required.
1621 */
1622 if (MACH_PORT_UREFS_UNDERFLOW(urefs, srdelta)) {
1623 ip_unlock(port);
1624 goto invalid_value;
1625 }
d190cdc3
A
1626
1627 if (urefs == MACH_PORT_UREFS_MAX) {
1628 /*
1629 * urefs are pegged due to an overflow
1630 * only a delta removing all refs at once can change it
1631 */
0a7de745 1632 if (srdelta != (-((mach_port_delta_t)MACH_PORT_UREFS_MAX))) {
d190cdc3 1633 srdelta = 0;
0a7de745 1634 }
d190cdc3
A
1635 }
1636
39236c6e
A
1637 if ((urefs + srdelta) == 0) {
1638 if (--port->ip_srights == 0) {
1639 nsrequest = port->ip_nsrequest;
1640 if (nsrequest != IP_NULL) {
1641 port->ip_nsrequest = IP_NULL;
1642 mscount = port->ip_mscount;
1643 }
1644 }
1645 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE);
0a7de745
A
1646 entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK |
1647 MACH_PORT_TYPE_SEND);
39236c6e
A
1648 } else {
1649 entry->ie_bits = bits + srdelta;
1650 }
1651 }
1652
1653 /*
1654 * Now destroy the receive right. Update space and
1655 * entry accordingly.
1656 */
1657
1658 bits = entry->ie_bits;
1659 if (bits & MACH_PORT_TYPE_SEND) {
1660 assert(IE_BITS_UREFS(bits) > 0);
d190cdc3 1661 assert(IE_BITS_UREFS(bits) <= MACH_PORT_UREFS_MAX);
39236c6e
A
1662
1663 if (port->ip_pdrequest != NULL) {
1664 /*
1665 * Since another task has requested a
1666 * destroy notification for this port, it
1667 * isn't actually being destroyed - the receive
1668 * right is just being moved to another task.
1669 * Since we still have one or more send rights,
1670 * we need to record the loss of the receive
1671 * right and enter the remaining send right
1672 * into the hash table.
1673 */
1674 ipc_entry_modified(space, name, entry);
1675 entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
cb323159
A
1676 entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
1677 ipc_hash_insert(space, ip_to_object(port),
39236c6e
A
1678 name, entry);
1679 ip_reference(port);
1680 } else {
1681 /*
1682 * The remaining send right turns into a
1683 * dead name. Notice we don't decrement
1684 * ip_srights, generate a no-senders notif,
1685 * or use ipc_right_dncancel, because the
1686 * port is destroyed "first".
1687 */
1688 bits &= ~IE_BITS_TYPE_MASK;
cb323159 1689 bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE);
39236c6e
A
1690 if (entry->ie_request) {
1691 entry->ie_request = IE_REQ_NONE;
0a7de745 1692 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
d190cdc3 1693 bits++; /* increment urefs */
0a7de745 1694 }
39236c6e
A
1695 }
1696 entry->ie_bits = bits;
1697 entry->ie_object = IO_NULL;
1698 ipc_entry_modified(space, name, entry);
1699 }
1700 } else {
1701 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
1702 assert(IE_BITS_UREFS(bits) == 0);
1703 request = ipc_right_request_cancel_macro(space, port,
0a7de745 1704 name, entry);
39236c6e
A
1705 entry->ie_object = IO_NULL;
1706 ipc_entry_dealloc(space, name, entry);
1707 }
1708
1709 /* Unlock space */
1710 is_write_unlock(space);
1711
0a7de745 1712 if (nsrequest != IP_NULL) {
39236c6e 1713 ipc_notify_no_senders(nsrequest, mscount);
0a7de745 1714 }
39236c6e 1715
0a7de745 1716 ipc_port_destroy(port); /* clears receiver, consumes ref, unlocks */
39236c6e 1717
0a7de745 1718 if (request != IP_NULL) {
39236c6e 1719 ipc_notify_port_deleted(request, name);
0a7de745
A
1720 }
1721
39236c6e 1722 return KERN_SUCCESS;
0a7de745
A
1723
1724invalid_value:
39236c6e 1725 is_write_unlock(space);
d9a64523 1726 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE);
39236c6e 1727 return KERN_INVALID_VALUE;
39236c6e
A
1728}
1729
1730
1c79356b
A
1731/*
1732 * Routine: ipc_right_info
1733 * Purpose:
1734 * Retrieves information about the right.
1735 * Conditions:
316670eb
A
1736 * The space is active and write-locked.
1737 * The space is unlocked upon return.
1c79356b 1738 * Returns:
316670eb 1739 * KERN_SUCCESS Retrieved info
1c79356b
A
1740 */
1741
1742kern_return_t
1743ipc_right_info(
0a7de745
A
1744 ipc_space_t space,
1745 mach_port_name_t name,
1746 ipc_entry_t entry,
1747 mach_port_type_t *typep,
1748 mach_port_urefs_t *urefsp)
1c79356b 1749{
6d2010ae 1750 ipc_port_t port;
1c79356b 1751 ipc_entry_bits_t bits;
6d2010ae 1752 mach_port_type_t type = 0;
1c79356b
A
1753 ipc_port_request_index_t request;
1754
1755 bits = entry->ie_bits;
6d2010ae 1756 request = entry->ie_request;
cb323159 1757 port = ip_object_to_port(entry->ie_object);
1c79356b 1758
6d2010ae
A
1759 if (bits & MACH_PORT_TYPE_RECEIVE) {
1760 assert(IP_VALID(port));
1c79356b 1761
6d2010ae
A
1762 if (request != IE_REQ_NONE) {
1763 ip_lock(port);
cb323159 1764 require_ip_active(port);
6d2010ae
A
1765 type |= ipc_port_request_type(port, name, request);
1766 ip_unlock(port);
1767 }
316670eb 1768 is_write_unlock(space);
6d2010ae
A
1769 } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
1770 /*
1771 * validate port is still alive - if so, get request
1772 * types while we still have it locked. Otherwise,
1773 * recapture the (now dead) bits.
1774 */
0a7de745
A
1775 if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1776 if (request != IE_REQ_NONE) {
6d2010ae 1777 type |= ipc_port_request_type(port, name, request);
0a7de745 1778 }
6d2010ae 1779 ip_unlock(port);
316670eb 1780 is_write_unlock(space);
6d2010ae 1781 } else {
1c79356b
A
1782 bits = entry->ie_bits;
1783 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
316670eb
A
1784 is_write_unlock(space);
1785 ip_release(port);
6d2010ae 1786 }
316670eb
A
1787 } else {
1788 is_write_unlock(space);
1c79356b
A
1789 }
1790
6d2010ae 1791 type |= IE_BITS_TYPE(bits);
1c79356b
A
1792
1793 *typep = type;
1794 *urefsp = IE_BITS_UREFS(bits);
1795 return KERN_SUCCESS;
1796}
1797
1798/*
cb323159 1799 * Routine: ipc_right_copyin_check_reply
1c79356b 1800 * Purpose:
cb323159
A
1801 * Check if a subsequent ipc_right_copyin would succeed. Used only
1802 * by ipc_kmsg_copyin_header to check if reply_port can be copied in.
1803 * If the reply port is an immovable send right, it errors out.
1c79356b
A
1804 * Conditions:
1805 * The space is locked (read or write) and active.
1806 */
1807
1808boolean_t
cb323159 1809ipc_right_copyin_check_reply(
0a7de745 1810 __assert_only ipc_space_t space,
cb323159
A
1811 mach_port_name_t reply_name,
1812 ipc_entry_t reply_entry,
1813 mach_msg_type_name_t reply_type)
1c79356b
A
1814{
1815 ipc_entry_bits_t bits;
cb323159 1816 ipc_port_t reply_port;
1c79356b 1817
cb323159 1818 bits = reply_entry->ie_bits;
316670eb 1819 assert(is_active(space));
1c79356b 1820
cb323159 1821 switch (reply_type) {
0a7de745
A
1822 case MACH_MSG_TYPE_MAKE_SEND:
1823 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
2d21ac55 1824 return FALSE;
0a7de745 1825 }
2d21ac55
A
1826 break;
1827
0a7de745
A
1828 case MACH_MSG_TYPE_MAKE_SEND_ONCE:
1829 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
2d21ac55 1830 return FALSE;
0a7de745 1831 }
2d21ac55
A
1832 break;
1833
0a7de745 1834 case MACH_MSG_TYPE_MOVE_RECEIVE:
cb323159
A
1835 /* ipc_kmsg_copyin_header already filters it out */
1836 return FALSE;
1c79356b 1837
0a7de745
A
1838 case MACH_MSG_TYPE_COPY_SEND:
1839 case MACH_MSG_TYPE_MOVE_SEND:
1840 case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
1841 if (bits & MACH_PORT_TYPE_DEAD_NAME) {
1c79356b 1842 break;
0a7de745 1843 }
1c79356b 1844
0a7de745 1845 if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
1c79356b 1846 return FALSE;
0a7de745 1847 }
1c79356b 1848
cb323159
A
1849 reply_port = ip_object_to_port(reply_entry->ie_object);
1850 assert(reply_port != IP_NULL);
1c79356b 1851
fe8ab488
A
1852 /*
1853 * active status peek to avoid checks that will be skipped
1854 * on copyin for dead ports. Lock not held, so will not be
1855 * atomic (but once dead, there's no going back).
1856 */
cb323159 1857 if (!ip_active(reply_port)) {
1c79356b
A
1858 break;
1859 }
1860
cb323159
A
1861 /*
1862 * Can't copyin a send right that is marked immovable. This bit
1863 * is set only during port creation and never unset. So it can
1864 * be read without a lock.
1865 */
1866 if (reply_port->ip_immovable_send) {
1867 mach_port_guard_exception(reply_name, 0, 0, kGUARD_EXC_IMMOVABLE);
1868 return FALSE;
1869 }
1870
1871 if (reply_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
0a7de745 1872 if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
1c79356b 1873 return FALSE;
0a7de745 1874 }
1c79356b 1875 } else {
0a7de745 1876 if ((bits & MACH_PORT_TYPE_SEND) == 0) {
1c79356b 1877 return FALSE;
0a7de745 1878 }
1c79356b
A
1879 }
1880
1881 break;
0a7de745 1882 }
1c79356b 1883
0a7de745 1884 default:
1c79356b
A
1885 panic("ipc_right_copyin_check: strange rights");
1886 }
1887
1888 return TRUE;
1889}
1890
cb323159
A
1891/*
1892 * Routine: ipc_right_copyin_check_guard_locked
1893 * Purpose:
1894 * Check if the port is guarded and the guard
1895 * value matches the one passed in the arguments.
1896 * If MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND is set,
1897 * check if the port is unguarded.
1898 * Conditions:
1899 * The port is locked.
1900 * Returns:
1901 * KERN_SUCCESS Port is either unguarded
1902 * or guarded with expected value
1903 * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
1904 * This also raises a EXC_GUARD exception.
1905 */
1906static kern_return_t
1907ipc_right_copyin_check_guard_locked(
1908 mach_port_name_t name,
1909 ipc_port_t port,
1910 mach_port_context_t context,
1911 mach_msg_guard_flags_t *guard_flags)
1912{
1913 mach_msg_guard_flags_t flags = *guard_flags;
1914 if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) && !port->ip_guarded && !context) {
1915 return KERN_SUCCESS;
1916 } else if (port->ip_guarded && (port->ip_context == context)) {
1917 return KERN_SUCCESS;
1918 }
1919
1920 /* Incorrect guard; Raise exception */
1921 mach_port_guard_exception(name, context, port->ip_context, kGUARD_EXC_INCORRECT_GUARD);
1922 return KERN_INVALID_ARGUMENT;
1923}
1924
1c79356b
A
1925/*
1926 * Routine: ipc_right_copyin
1927 * Purpose:
1928 * Copyin a capability from a space.
1929 * If successful, the caller gets a ref
1930 * for the resulting object, unless it is IO_DEAD,
1931 * and possibly a send-once right which should
1932 * be used in a port-deleted notification.
1933 *
1934 * If deadok is not TRUE, the copyin operation
1935 * will fail instead of producing IO_DEAD.
1936 *
1937 * The entry is never deallocated (except
1938 * when KERN_INVALID_NAME), so the caller
1939 * should deallocate the entry if its type
1940 * is MACH_PORT_TYPE_NONE.
1941 * Conditions:
1942 * The space is write-locked and active.
1943 * Returns:
1944 * KERN_SUCCESS Acquired an object, possibly IO_DEAD.
1945 * KERN_INVALID_RIGHT Name doesn't denote correct right.
cb323159
A
1946 * KERN_INVALID_CAPABILITY Trying to move an kobject port or an immovable right
1947 * KERN_INVALID_ARGUMENT Port is unguarded or guard mismatch
1c79356b
A
1948 */
1949
1950kern_return_t
1951ipc_right_copyin(
cb323159
A
1952 ipc_space_t space,
1953 mach_port_name_t name,
1954 ipc_entry_t entry,
1955 mach_msg_type_name_t msgt_name,
1956 ipc_right_copyin_flags_t flags,
1957 ipc_object_t *objectp,
1958 ipc_port_t *sorightp,
1959 ipc_port_t *releasep,
1960 int *assertcntp,
1961 mach_port_context_t context,
1962 mach_msg_guard_flags_t *guard_flags)
1c79356b
A
1963{
1964 ipc_entry_bits_t bits;
316670eb 1965 ipc_port_t port;
cb323159
A
1966 kern_return_t kr;
1967 boolean_t deadok = flags & IPC_RIGHT_COPYIN_FLAGS_DEADOK? TRUE : FALSE;
1968 boolean_t allow_imm_send = flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND? TRUE : FALSE;
fe8ab488 1969
316670eb 1970 *releasep = IP_NULL;
39236c6e 1971 *assertcntp = 0;
39236c6e 1972
1c79356b
A
1973 bits = entry->ie_bits;
1974
316670eb 1975 assert(is_active(space));
1c79356b
A
1976
1977 switch (msgt_name) {
0a7de745
A
1978 case MACH_MSG_TYPE_MAKE_SEND: {
1979 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
1c79356b 1980 goto invalid_right;
0a7de745 1981 }
1c79356b 1982
cb323159 1983 port = ip_object_to_port(entry->ie_object);
1c79356b
A
1984 assert(port != IP_NULL);
1985
1986 ip_lock(port);
1c79356b
A
1987 assert(port->ip_receiver_name == name);
1988 assert(port->ip_receiver == space);
1989
cb323159 1990 ipc_port_make_send_locked(port);
1c79356b
A
1991 ip_unlock(port);
1992
cb323159 1993 *objectp = ip_to_object(port);
1c79356b
A
1994 *sorightp = IP_NULL;
1995 break;
0a7de745 1996 }
1c79356b 1997
0a7de745
A
1998 case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
1999 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
1c79356b 2000 goto invalid_right;
0a7de745 2001 }
1c79356b 2002
cb323159 2003 port = ip_object_to_port(entry->ie_object);
1c79356b
A
2004 assert(port != IP_NULL);
2005
2006 ip_lock(port);
cb323159 2007 require_ip_active(port);
1c79356b
A
2008 assert(port->ip_receiver_name == name);
2009 assert(port->ip_receiver == space);
2010
cb323159 2011 ipc_port_make_sonce_locked(port);
1c79356b
A
2012 ip_unlock(port);
2013
cb323159 2014 *objectp = ip_to_object(port);
1c79356b
A
2015 *sorightp = IP_NULL;
2016 break;
0a7de745 2017 }
1c79356b 2018
0a7de745 2019 case MACH_MSG_TYPE_MOVE_RECEIVE: {
6d2010ae 2020 ipc_port_t request = IP_NULL;
1c79356b 2021
0a7de745 2022 if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
1c79356b 2023 goto invalid_right;
0a7de745 2024 }
1c79356b 2025
00867663
A
2026 /*
2027 * Disallow moving receive-right kobjects, e.g. mk_timer ports
2028 * The ipc_port structure uses the kdata union of kobject and
2029 * imp_task exclusively. Thus, general use of a kobject port as
2030 * a receive right can cause type confusion in the importance
2031 * code.
2032 */
2033 if (io_kotype(entry->ie_object) != IKOT_NONE) {
2034 /*
2035 * Distinguish an invalid right, e.g., trying to move
2036 * a send right as a receive right, from this
2037 * situation which is, "This is a valid receive right,
2038 * but it's also a kobject and you can't move it."
2039 */
cb323159 2040 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
00867663
A
2041 return KERN_INVALID_CAPABILITY;
2042 }
2043
cb323159 2044 port = ip_object_to_port(entry->ie_object);
1c79356b
A
2045 assert(port != IP_NULL);
2046
2047 ip_lock(port);
cb323159 2048 require_ip_active(port);
1c79356b
A
2049 assert(port->ip_receiver_name == name);
2050 assert(port->ip_receiver == space);
2051
cb323159
A
2052 if (port->ip_immovable_receive) {
2053 assert(port->ip_receiver != ipc_space_kernel);
2054 ip_unlock(port);
2055 assert(current_task() != kernel_task);
2056 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
2057 return KERN_INVALID_CAPABILITY;
2058 }
2059
2060 if (guard_flags != NULL) {
2061 kr = ipc_right_copyin_check_guard_locked(name, port, context, guard_flags);
2062 if (kr != KERN_SUCCESS) {
2063 ip_unlock(port);
2064 return kr;
2065 }
2066 }
2067
1c79356b
A
2068 if (bits & MACH_PORT_TYPE_SEND) {
2069 assert(IE_BITS_TYPE(bits) ==
0a7de745 2070 MACH_PORT_TYPE_SEND_RECEIVE);
1c79356b
A
2071 assert(IE_BITS_UREFS(bits) > 0);
2072 assert(port->ip_srights > 0);
2073
cb323159 2074 ipc_hash_insert(space, ip_to_object(port),
0a7de745 2075 name, entry);
1c79356b
A
2076 ip_reference(port);
2077 } else {
2078 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
2079 assert(IE_BITS_UREFS(bits) == 0);
2080
6d2010ae 2081 request = ipc_right_request_cancel_macro(space, port,
0a7de745 2082 name, entry);
1c79356b
A
2083 entry->ie_object = IO_NULL;
2084 }
0a7de745 2085 entry->ie_bits = bits & ~MACH_PORT_TYPE_RECEIVE;
cb323159 2086 entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
316670eb 2087 ipc_entry_modified(space, name, entry);
1c79356b 2088
cb323159 2089 /* ipc_port_clear_receiver unguards the port and clears the ip_immovable_receive bit */
39037602 2090 (void)ipc_port_clear_receiver(port, FALSE); /* don't destroy the port/mqueue */
cb323159
A
2091 if (guard_flags != NULL) {
2092 /* this flag will be cleared during copyout */
2093 *guard_flags = *guard_flags | MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND;
2094 }
39236c6e
A
2095
2096#if IMPORTANCE_INHERITANCE
2097 /*
2098 * Account for boosts the current task is going to lose when
2099 * copying this right in. Tempowner ports have either not
2100 * been accounting to any task (and therefore are already in
2101 * "limbo" state w.r.t. assertions) or to some other specific
2102 * task. As we have no way to drop the latter task's assertions
2103 * here, We'll deduct those when we enqueue it on its
2104 * destination port (see ipc_port_check_circularity()).
2105 */
2106 if (port->ip_tempowner == 0) {
fe8ab488 2107 assert(IIT_NULL == port->ip_imp_task);
39236c6e
A
2108
2109 /* ports in limbo have to be tempowner */
2110 port->ip_tempowner = 1;
2111 *assertcntp = port->ip_impcount;
2112 }
2113#endif /* IMPORTANCE_INHERITANCE */
2114
1c79356b
A
2115 ip_unlock(port);
2116
cb323159 2117 *objectp = ip_to_object(port);
6d2010ae 2118 *sorightp = request;
1c79356b 2119 break;
0a7de745 2120 }
1c79356b 2121
0a7de745
A
2122 case MACH_MSG_TYPE_COPY_SEND: {
2123 if (bits & MACH_PORT_TYPE_DEAD_NAME) {
1c79356b 2124 goto copy_dead;
0a7de745 2125 }
1c79356b
A
2126
2127 /* allow for dead send-once rights */
2128
0a7de745 2129 if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
1c79356b 2130 goto invalid_right;
0a7de745 2131 }
1c79356b
A
2132
2133 assert(IE_BITS_UREFS(bits) > 0);
2134
cb323159 2135 port = ip_object_to_port(entry->ie_object);
1c79356b
A
2136 assert(port != IP_NULL);
2137
0a7de745 2138 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b 2139 bits = entry->ie_bits;
316670eb 2140 *releasep = port;
1c79356b
A
2141 goto copy_dead;
2142 }
2143 /* port is locked and active */
2144
2145 if ((bits & MACH_PORT_TYPE_SEND) == 0) {
2146 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
2147 assert(port->ip_sorights > 0);
2148
2149 ip_unlock(port);
2150 goto invalid_right;
2151 }
2152
cb323159
A
2153 if (!allow_imm_send && port->ip_immovable_send) {
2154 ip_unlock(port);
2155 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
2156 return KERN_INVALID_CAPABILITY;
2157 }
1c79356b 2158
cb323159 2159 ipc_port_copy_send_locked(port);
1c79356b
A
2160 ip_unlock(port);
2161
cb323159 2162 *objectp = ip_to_object(port);
1c79356b
A
2163 *sorightp = IP_NULL;
2164 break;
0a7de745 2165 }
1c79356b 2166
0a7de745 2167 case MACH_MSG_TYPE_MOVE_SEND: {
6d2010ae 2168 ipc_port_t request = IP_NULL;
1c79356b 2169
0a7de745 2170 if (bits & MACH_PORT_TYPE_DEAD_NAME) {
1c79356b 2171 goto move_dead;
0a7de745 2172 }
1c79356b
A
2173
2174 /* allow for dead send-once rights */
2175
0a7de745 2176 if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
1c79356b 2177 goto invalid_right;
0a7de745 2178 }
1c79356b
A
2179
2180 assert(IE_BITS_UREFS(bits) > 0);
2181
cb323159 2182 port = ip_object_to_port(entry->ie_object);
1c79356b
A
2183 assert(port != IP_NULL);
2184
0a7de745 2185 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
1c79356b 2186 bits = entry->ie_bits;
316670eb 2187 *releasep = port;
1c79356b
A
2188 goto move_dead;
2189 }
2190 /* port is locked and active */
2191
2192 if ((bits & MACH_PORT_TYPE_SEND) == 0) {
2193 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
2194 assert(port->ip_sorights > 0);
2195
2196 ip_unlock(port);
2197 goto invalid_right;
2198 }
2199
cb323159
A
2200 if (!allow_imm_send && port->ip_immovable_send) {
2201 ip_unlock(port);
2202 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
2203 return KERN_INVALID_CAPABILITY;
2204 }
1c79356b
A
2205
2206 if (IE_BITS_UREFS(bits) == 1) {
cb323159 2207 assert(port->ip_srights > 0);
1c79356b
A
2208 if (bits & MACH_PORT_TYPE_RECEIVE) {
2209 assert(port->ip_receiver_name == name);
2210 assert(port->ip_receiver == space);
2211 assert(IE_BITS_TYPE(bits) ==
0a7de745 2212 MACH_PORT_TYPE_SEND_RECEIVE);
1c79356b
A
2213
2214 ip_reference(port);
2215 } else {
2216 assert(IE_BITS_TYPE(bits) ==
0a7de745 2217 MACH_PORT_TYPE_SEND);
1c79356b 2218
6d2010ae 2219 request = ipc_right_request_cancel_macro(space, port,
0a7de745 2220 name, entry);
cb323159 2221 ipc_hash_delete(space, ip_to_object(port),
0a7de745 2222 name, entry);
1c79356b 2223 entry->ie_object = IO_NULL;
d190cdc3 2224 /* transfer entry's reference to caller */
1c79356b 2225 }
0a7de745
A
2226 entry->ie_bits = bits & ~
2227 (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND);
1c79356b 2228 } else {
cb323159 2229 ipc_port_copy_send_locked(port);
d190cdc3 2230 /* if urefs are pegged due to overflow, leave them pegged */
0a7de745
A
2231 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
2232 entry->ie_bits = bits - 1; /* decrement urefs */
2233 }
1c79356b 2234 }
d190cdc3 2235
316670eb 2236 ipc_entry_modified(space, name, entry);
1c79356b
A
2237 ip_unlock(port);
2238
cb323159 2239 *objectp = ip_to_object(port);
6d2010ae 2240 *sorightp = request;
1c79356b 2241 break;
0a7de745 2242 }
1c79356b 2243
0a7de745 2244 case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
6d2010ae 2245 ipc_port_t request;
1c79356b 2246
0a7de745 2247 if (bits & MACH_PORT_TYPE_DEAD_NAME) {
1c79356b 2248 goto move_dead;
0a7de745 2249 }
1c79356b
A
2250
2251 /* allow for dead send rights */
2252
0a7de745 2253 if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) {
1c79356b 2254 goto invalid_right;
0a7de745 2255 }
1c79356b
A
2256
2257 assert(IE_BITS_UREFS(bits) > 0);
2258
cb323159 2259 port = ip_object_to_port(entry->ie_object);
1c79356b
A
2260 assert(port != IP_NULL);
2261
0a7de745 2262 if (ipc_right_check(space, port, name, entry, flags)) {
1c79356b 2263 bits = entry->ie_bits;
3e170ce0 2264 *releasep = port;
1c79356b
A
2265 goto move_dead;
2266 }
0a7de745
A
2267 /*
2268 * port is locked, but may not be active:
2269 * Allow copyin of inactive ports with no dead name request and treat it
2270 * as if the copyin of the port was successful and port became inactive
2271 * later.
2272 */
1c79356b
A
2273
2274 if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
2275 assert(bits & MACH_PORT_TYPE_SEND);
2276 assert(port->ip_srights > 0);
2277
2278 ip_unlock(port);
2279 goto invalid_right;
2280 }
2281
cb323159
A
2282 if (!allow_imm_send && port->ip_immovable_send) {
2283 ip_unlock(port);
2284 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
2285 return KERN_INVALID_CAPABILITY;
2286 }
2287
1c79356b
A
2288 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
2289 assert(IE_BITS_UREFS(bits) == 1);
2290 assert(port->ip_sorights > 0);
2291
6d2010ae 2292 request = ipc_right_request_cancel_macro(space, port, name, entry);
1c79356b
A
2293 ip_unlock(port);
2294
2295 entry->ie_object = IO_NULL;
0a7de745
A
2296 entry->ie_bits = bits & ~
2297 (IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE);
316670eb 2298 ipc_entry_modified(space, name, entry);
cb323159 2299 *objectp = ip_to_object(port);
6d2010ae 2300 *sorightp = request;
1c79356b 2301 break;
0a7de745 2302 }
1c79356b 2303
0a7de745
A
2304 default:
2305invalid_right:
1c79356b
A
2306 return KERN_INVALID_RIGHT;
2307 }
2308
2309 return KERN_SUCCESS;
2310
0a7de745 2311copy_dead:
1c79356b
A
2312 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
2313 assert(IE_BITS_UREFS(bits) > 0);
6d2010ae 2314 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
2315 assert(entry->ie_object == 0);
2316
0a7de745 2317 if (!deadok) {
1c79356b 2318 goto invalid_right;
0a7de745 2319 }
1c79356b
A
2320
2321 *objectp = IO_DEAD;
2322 *sorightp = IP_NULL;
2323 return KERN_SUCCESS;
2324
0a7de745 2325move_dead:
1c79356b
A
2326 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
2327 assert(IE_BITS_UREFS(bits) > 0);
6d2010ae 2328 assert(entry->ie_request == IE_REQ_NONE);
1c79356b
A
2329 assert(entry->ie_object == 0);
2330
0a7de745 2331 if (!deadok) {
1c79356b 2332 goto invalid_right;
0a7de745 2333 }
1c79356b
A
2334
2335 if (IE_BITS_UREFS(bits) == 1) {
2336 bits &= ~MACH_PORT_TYPE_DEAD_NAME;
2337 }
d190cdc3 2338 /* if urefs are pegged due to overflow, leave them pegged */
0a7de745
A
2339 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
2340 entry->ie_bits = bits - 1; /* decrement urefs */
2341 }
316670eb 2342 ipc_entry_modified(space, name, entry);
1c79356b
A
2343 *objectp = IO_DEAD;
2344 *sorightp = IP_NULL;
2345 return KERN_SUCCESS;
1c79356b
A
2346}
2347
1c79356b 2348/*
fe8ab488 2349 * Routine: ipc_right_copyin_two_move_sends
1c79356b
A
2350 * Purpose:
2351 * Like ipc_right_copyin with MACH_MSG_TYPE_MOVE_SEND
2352 * and deadok == FALSE, except that this moves two
2353 * send rights at once.
2354 * Conditions:
2355 * The space is write-locked and active.
2356 * The object is returned with two refs/send rights.
2357 * Returns:
2358 * KERN_SUCCESS Acquired an object.
2359 * KERN_INVALID_RIGHT Name doesn't denote correct right.
2360 */
fe8ab488 2361static
1c79356b 2362kern_return_t
fe8ab488 2363ipc_right_copyin_two_move_sends(
0a7de745
A
2364 ipc_space_t space,
2365 mach_port_name_t name,
2366 ipc_entry_t entry,
2367 ipc_object_t *objectp,
2368 ipc_port_t *sorightp,
2369 ipc_port_t *releasep)
1c79356b
A
2370{
2371 ipc_entry_bits_t bits;
2372 mach_port_urefs_t urefs;
2373 ipc_port_t port;
6d2010ae 2374 ipc_port_t request = IP_NULL;
1c79356b 2375
316670eb
A
2376 *releasep = IP_NULL;
2377
2378 assert(is_active(space));
1c79356b
A
2379
2380 bits = entry->ie_bits;
2381
0a7de745 2382 if ((bits & MACH_PORT_TYPE_SEND) == 0) {
1c79356b 2383 goto invalid_right;
0a7de745 2384 }
1c79356b
A
2385
2386 urefs = IE_BITS_UREFS(bits);
0a7de745 2387 if (urefs < 2) {
1c79356b 2388 goto invalid_right;
0a7de745 2389 }
1c79356b 2390
cb323159 2391 port = ip_object_to_port(entry->ie_object);
1c79356b
A
2392 assert(port != IP_NULL);
2393
0a7de745 2394 if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
316670eb 2395 *releasep = port;
1c79356b
A
2396 goto invalid_right;
2397 }
2398 /* port is locked and active */
2399
cb323159
A
2400 if (urefs > 2) {
2401 /*
2402 * We are moving 2 urefs as naked send rights, which is decomposed as:
2403 * - two copy sends (which doesn't affect the make send count)
2404 * - decrementing the local urefs twice.
2405 */
2406 ipc_port_copy_send_locked(port);
2407 ipc_port_copy_send_locked(port);
2408 /* if urefs are pegged due to overflow, leave them pegged */
2409 if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
2410 entry->ie_bits = bits - 2; /* decrement urefs */
2411 }
2412 } else {
2413 /*
2414 * We have exactly 2 send rights for this port in this space,
2415 * which means that we will liberate the naked send right held
2416 * by this entry.
2417 *
2418 * However refcounting rules around entries are that naked send rights
2419 * on behalf of spaces do not have an associated port reference,
2420 * so we need to donate one ...
2421 */
2422 ipc_port_copy_send_locked(port);
1c79356b 2423
1c79356b
A
2424 if (bits & MACH_PORT_TYPE_RECEIVE) {
2425 assert(port->ip_receiver_name == name);
2426 assert(port->ip_receiver == space);
2427 assert(IE_BITS_TYPE(bits) ==
0a7de745 2428 MACH_PORT_TYPE_SEND_RECEIVE);
1c79356b 2429
cb323159 2430 /* ... that we inject manually when the entry stays alive */
1c79356b
A
2431 ip_reference(port);
2432 } else {
2433 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
2434
cb323159 2435 /* ... that we steal from the entry when it dies */
6d2010ae 2436 request = ipc_right_request_cancel_macro(space, port,
0a7de745 2437 name, entry);
1c79356b 2438
cb323159 2439 ipc_hash_delete(space, ip_to_object(port),
0a7de745 2440 name, entry);
1c79356b
A
2441 entry->ie_object = IO_NULL;
2442 }
cb323159 2443
0a7de745 2444 entry->ie_bits = bits & ~(IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND);
1c79356b 2445 }
316670eb
A
2446 ipc_entry_modified(space, name, entry);
2447
1c79356b
A
2448 ip_unlock(port);
2449
cb323159 2450 *objectp = ip_to_object(port);
6d2010ae 2451 *sorightp = request;
1c79356b
A
2452 return KERN_SUCCESS;
2453
0a7de745 2454invalid_right:
1c79356b
A
2455 return KERN_INVALID_RIGHT;
2456}
2457
fe8ab488
A
2458
2459/*
2460 * Routine: ipc_right_copyin_two
2461 * Purpose:
2462 * Like ipc_right_copyin with two dispositions,
2463 * each of which results in a send or send-once right,
2464 * and deadok = FALSE.
2465 * Conditions:
2466 * The space is write-locked and active.
2467 * The object is returned with two refs/rights.
cb323159 2468 * Msgt_one refers to the dest_type
fe8ab488
A
2469 * Returns:
2470 * KERN_SUCCESS Acquired an object.
2471 * KERN_INVALID_RIGHT Name doesn't denote correct right(s).
2472 * KERN_INVALID_CAPABILITY Name doesn't denote correct right for msgt_two.
2473 */
2474kern_return_t
2475ipc_right_copyin_two(
cb323159
A
2476 ipc_space_t space,
2477 mach_port_name_t name,
2478 ipc_entry_t entry,
2479 mach_msg_type_name_t msgt_one,
2480 mach_msg_type_name_t msgt_two,
2481 ipc_object_t *objectp,
2482 ipc_port_t *sorightp,
2483 ipc_port_t *releasep)
fe8ab488 2484{
fe8ab488 2485 kern_return_t kr;
fe8ab488 2486 int assertcnt = 0;
fe8ab488
A
2487
2488 assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_one));
2489 assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_two));
0a7de745 2490
fe8ab488
A
2491 /*
2492 * This is a little tedious to make atomic, because
2493 * there are 25 combinations of valid dispositions.
2494 * However, most are easy.
2495 */
2496
2497 /*
2498 * If either is move-sonce, then there must be an error.
2499 */
2500 if (msgt_one == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
2501 msgt_two == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
2502 return KERN_INVALID_RIGHT;
2503 }
2504
2505 if ((msgt_one == MACH_MSG_TYPE_MAKE_SEND) ||
2506 (msgt_one == MACH_MSG_TYPE_MAKE_SEND_ONCE) ||
2507 (msgt_two == MACH_MSG_TYPE_MAKE_SEND) ||
2508 (msgt_two == MACH_MSG_TYPE_MAKE_SEND_ONCE)) {
2509 /*
2510 * One of the dispositions needs a receive right.
2511 *
2512 * If the copyin below succeeds, we know the receive
2513 * right is there (because the pre-validation of
2514 * the second disposition already succeeded in our
2515 * caller).
2516 *
2517 * Hence the port is not in danger of dying.
2518 */
2519 ipc_object_t object_two;
2520
fe8ab488 2521 kr = ipc_right_copyin(space, name, entry,
cb323159 2522 msgt_one, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
0a7de745 2523 objectp, sorightp, releasep,
cb323159 2524 &assertcnt, 0, NULL);
fe8ab488 2525 assert(assertcnt == 0);
fe8ab488
A
2526 if (kr != KERN_SUCCESS) {
2527 return kr;
2528 }
2529
2530 assert(IO_VALID(*objectp));
2531 assert(*sorightp == IP_NULL);
2532 assert(*releasep == IP_NULL);
2533
2534 /*
2535 * Now copyin the second (previously validated)
2536 * disposition. The result can't be a dead port,
2537 * as no valid disposition can make us lose our
2538 * receive right.
2539 */
fe8ab488 2540 kr = ipc_right_copyin(space, name, entry,
0a7de745
A
2541 msgt_two, IPC_RIGHT_COPYIN_FLAGS_NONE,
2542 &object_two, sorightp, releasep,
cb323159 2543 &assertcnt, 0, NULL);
fe8ab488 2544 assert(assertcnt == 0);
fe8ab488
A
2545 assert(kr == KERN_SUCCESS);
2546 assert(*sorightp == IP_NULL);
2547 assert(*releasep == IP_NULL);
2548 assert(object_two == *objectp);
2549 assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
fe8ab488 2550 } else if ((msgt_one == MACH_MSG_TYPE_MOVE_SEND) &&
0a7de745 2551 (msgt_two == MACH_MSG_TYPE_MOVE_SEND)) {
fe8ab488
A
2552 /*
2553 * This is an easy case. Just use our
2554 * handy-dandy special-purpose copyin call
2555 * to get two send rights for the price of one.
2556 */
2557 kr = ipc_right_copyin_two_move_sends(space, name, entry,
0a7de745
A
2558 objectp, sorightp,
2559 releasep);
fe8ab488
A
2560 if (kr != KERN_SUCCESS) {
2561 return kr;
2562 }
fe8ab488
A
2563 } else {
2564 mach_msg_type_name_t msgt_name;
2565
2566 /*
2567 * Must be either a single move-send and a
2568 * copy-send, or two copy-send dispositions.
2569 * Use the disposition with the greatest side
2570 * effects for the actual copyin - then just
2571 * duplicate the send right you get back.
2572 */
2573 if (msgt_one == MACH_MSG_TYPE_MOVE_SEND ||
2574 msgt_two == MACH_MSG_TYPE_MOVE_SEND) {
2575 msgt_name = MACH_MSG_TYPE_MOVE_SEND;
2576 } else {
2577 msgt_name = MACH_MSG_TYPE_COPY_SEND;
2578 }
2579
fe8ab488 2580 kr = ipc_right_copyin(space, name, entry,
cb323159 2581 msgt_name, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
0a7de745 2582 objectp, sorightp, releasep,
cb323159 2583 &assertcnt, 0, NULL);
fe8ab488 2584 assert(assertcnt == 0);
fe8ab488
A
2585 if (kr != KERN_SUCCESS) {
2586 return kr;
2587 }
2588
2589 /*
2590 * Copy the right we got back. If it is dead now,
2591 * that's OK. Neither right will be usable to send
2592 * a message anyway.
2593 */
cb323159 2594 (void)ipc_port_copy_send(ip_object_to_port(*objectp));
fe8ab488
A
2595 }
2596
fe8ab488
A
2597 return KERN_SUCCESS;
2598}
2599
2600
1c79356b
A
2601/*
2602 * Routine: ipc_right_copyout
2603 * Purpose:
2604 * Copyout a capability to a space.
2605 * If successful, consumes a ref for the object.
2606 *
2607 * Always succeeds when given a newly-allocated entry,
2608 * because user-reference overflow isn't a possibility.
2609 *
2610 * If copying out the object would cause the user-reference
cb323159
A
2611 * count in the entry to overflow, then the user-reference
2612 * count is left pegged to its maximum value and the copyout
2613 * succeeds anyway.
1c79356b
A
2614 * Conditions:
2615 * The space is write-locked and active.
2616 * The object is locked and active.
2617 * The object is unlocked; the space isn't.
2618 * Returns:
2619 * KERN_SUCCESS Copied out capability.
1c79356b
A
2620 */
2621
2622kern_return_t
2623ipc_right_copyout(
0a7de745
A
2624 ipc_space_t space,
2625 mach_port_name_t name,
2626 ipc_entry_t entry,
2627 mach_msg_type_name_t msgt_name,
cb323159
A
2628 mach_port_context_t *context,
2629 mach_msg_guard_flags_t *guard_flags,
0a7de745 2630 ipc_object_t object)
1c79356b
A
2631{
2632 ipc_entry_bits_t bits;
2633 ipc_port_t port;
2634
2635 bits = entry->ie_bits;
2636
2637 assert(IO_VALID(object));
2638 assert(io_otype(object) == IOT_PORT);
2639 assert(io_active(object));
2640 assert(entry->ie_object == object);
2641
cb323159 2642 port = ip_object_to_port(object);
1c79356b
A
2643
2644 switch (msgt_name) {
0a7de745 2645 case MACH_MSG_TYPE_PORT_SEND_ONCE:
d190cdc3 2646
1c79356b 2647 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
d190cdc3 2648 assert(IE_BITS_UREFS(bits) == 0);
1c79356b
A
2649 assert(port->ip_sorights > 0);
2650
5ba3f43e 2651 if (port->ip_specialreply) {
d9a64523 2652 ipc_port_adjust_special_reply_port_locked(port,
0a7de745 2653 current_thread()->ith_knote, IPC_PORT_ADJUST_SR_LINK_WORKLOOP, FALSE);
5ba3f43e
A
2654 /* port unlocked on return */
2655 } else {
2656 ip_unlock(port);
2657 }
1c79356b 2658
d190cdc3 2659 entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1); /* set urefs to 1 */
316670eb 2660 ipc_entry_modified(space, name, entry);
1c79356b
A
2661 break;
2662
0a7de745 2663 case MACH_MSG_TYPE_PORT_SEND:
1c79356b
A
2664 assert(port->ip_srights > 0);
2665
2666 if (bits & MACH_PORT_TYPE_SEND) {
2667 mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
2668
2669 assert(port->ip_srights > 1);
2670 assert(urefs > 0);
d190cdc3 2671 assert(urefs <= MACH_PORT_UREFS_MAX);
1c79356b 2672
d190cdc3
A
2673 if (urefs == MACH_PORT_UREFS_MAX) {
2674 /*
2675 * leave urefs pegged to maximum,
2676 * consume send right and ref
2677 */
1c79356b 2678
d190cdc3 2679 port->ip_srights--;
1c79356b 2680 ip_unlock(port);
d190cdc3
A
2681 ip_release(port);
2682 return KERN_SUCCESS;
1c79356b 2683 }
d190cdc3
A
2684
2685 /* consume send right and ref */
1c79356b 2686 port->ip_srights--;
1c79356b 2687 ip_unlock(port);
316670eb 2688 ip_release(port);
1c79356b
A
2689 } else if (bits & MACH_PORT_TYPE_RECEIVE) {
2690 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
2691 assert(IE_BITS_UREFS(bits) == 0);
2692
d190cdc3 2693 /* transfer send right to entry, consume ref */
1c79356b 2694 ip_unlock(port);
316670eb 2695 ip_release(port);
1c79356b
A
2696 } else {
2697 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
2698 assert(IE_BITS_UREFS(bits) == 0);
2699
2700 /* transfer send right and ref to entry */
2701 ip_unlock(port);
2702
2703 /* entry is locked holding ref, so can use port */
2704
cb323159 2705 ipc_hash_insert(space, ip_to_object(port), name, entry);
1c79356b
A
2706 }
2707
d190cdc3 2708 entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; /* increment urefs */
316670eb 2709 ipc_entry_modified(space, name, entry);
1c79356b
A
2710 break;
2711
0a7de745 2712 case MACH_MSG_TYPE_PORT_RECEIVE: {
1c79356b 2713 ipc_port_t dest;
39236c6e
A
2714#if IMPORTANCE_INHERITANCE
2715 natural_t assertcnt = port->ip_impcount;
2716#endif /* IMPORTANCE_INHERITANCE */
2717
1c79356b
A
2718 assert(port->ip_mscount == 0);
2719 assert(port->ip_receiver_name == MACH_PORT_NULL);
d9a64523
A
2720
2721 imq_lock(&port->ip_messages);
1c79356b
A
2722 dest = port->ip_destination;
2723
2724 port->ip_receiver_name = name;
2725 port->ip_receiver = space;
2726
cb323159 2727 struct knote *kn = current_thread()->ith_knote;
1c79356b 2728
cb323159
A
2729 if ((guard_flags != NULL) && ((*guard_flags & MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE) != 0)) {
2730 assert(port->ip_immovable_receive == 0);
2731 port->ip_guarded = 1;
2732 port->ip_strict_guard = 0;
2733 /* pseudo receive shouldn't set the receive right as immovable in the sender's space */
2734 if (kn != ITH_KNOTE_PSEUDO) {
2735 port->ip_immovable_receive = 1;
d9a64523 2736 }
cb323159
A
2737 port->ip_context = current_thread()->ith_msg_addr;
2738 *context = port->ip_context;
2739 *guard_flags = *guard_flags & ~MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND;
d9a64523
A
2740 }
2741
cb323159 2742 assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
1c79356b
A
2743 if (bits & MACH_PORT_TYPE_SEND) {
2744 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
2745 assert(IE_BITS_UREFS(bits) > 0);
2746 assert(port->ip_srights > 0);
1c79356b
A
2747 } else {
2748 assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
2749 assert(IE_BITS_UREFS(bits) == 0);
cb323159 2750 }
1c79356b 2751
cb323159
A
2752 boolean_t sync_bootstrap_checkin = FALSE;
2753 if (kn != ITH_KNOTE_PSEUDO && port->ip_sync_bootstrap_checkin) {
2754 sync_bootstrap_checkin = TRUE;
1c79356b 2755 }
cb323159
A
2756 if (!ITH_KNOTE_VALID(kn, MACH_MSG_TYPE_PORT_RECEIVE)) {
2757 kn = NULL;
2758 }
2759 ipc_port_adjust_port_locked(port, kn, sync_bootstrap_checkin);
2760 /* port & message queue are unlocked */
1c79356b 2761
cb323159
A
2762 if (bits & MACH_PORT_TYPE_SEND) {
2763 ip_release(port);
2764
2765 /* entry is locked holding ref, so can use port */
2766 ipc_hash_delete(space, ip_to_object(port), name, entry);
5ba3f43e 2767 }
cb323159
A
2768 entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
2769 ipc_entry_modified(space, name, entry);
5ba3f43e 2770
39236c6e
A
2771 if (dest != IP_NULL) {
2772#if IMPORTANCE_INHERITANCE
2773 /*
2774 * Deduct the assertion counts we contributed to
2775 * the old destination port. They've already
2776 * been reflected into the task as a result of
2777 * getting enqueued.
2778 */
2779 ip_lock(dest);
fe8ab488 2780 ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL);
39236c6e
A
2781 ip_unlock(dest);
2782#endif /* IMPORTANCE_INHERITANCE */
d9a64523
A
2783
2784 /* Drop turnstile ref on dest */
2785 ipc_port_send_turnstile_complete(dest);
316670eb 2786 ip_release(dest);
39236c6e 2787 }
1c79356b 2788 break;
0a7de745 2789 }
1c79356b 2790
0a7de745 2791 default:
1c79356b
A
2792 panic("ipc_right_copyout: strange rights");
2793 }
1c79356b
A
2794 return KERN_SUCCESS;
2795}