]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/mach_port.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / osfmk / ipc / mach_port.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_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/mach_port.c
67 * Author: Rich Draves
0a7de745 68 * Date: 1989
1c79356b
A
69 *
70 * Exported kernel calls. See mach/mach_port.defs.
71 */
72
73#include <mach_debug.h>
1c79356b
A
74
75#include <mach/port.h>
76#include <mach/kern_return.h>
77#include <mach/notify.h>
78#include <mach/mach_param.h>
79#include <mach/vm_param.h>
80#include <mach/vm_prot.h>
81#include <mach/vm_map.h>
82#include <kern/task.h>
83#include <kern/counters.h>
91447636
A
84#include <kern/thread.h>
85#include <kern/kalloc.h>
5ba3f43e 86#include <kern/exc_guard.h>
1c79356b
A
87#include <mach/mach_port_server.h>
88#include <vm/vm_map.h>
89#include <vm/vm_kern.h>
d9a64523 90#include <ipc/port.h>
1c79356b
A
91#include <ipc/ipc_entry.h>
92#include <ipc/ipc_space.h>
93#include <ipc/ipc_object.h>
94#include <ipc/ipc_notify.h>
95#include <ipc/ipc_port.h>
96#include <ipc/ipc_pset.h>
97#include <ipc/ipc_right.h>
98#include <ipc/ipc_kmsg.h>
99#include <kern/misc_protos.h>
2d21ac55
A
100#include <security/mac_mach_internal.h>
101
fe8ab488
A
102#if IMPORTANCE_INHERITANCE
103#include <ipc/ipc_importance.h>
104#endif
1c79356b 105
3e170ce0 106
1c79356b
A
107/* Zeroed template of qos flags */
108
0a7de745 109static mach_port_qos_t qos_template;
1c79356b
A
110
111/*
112 * Routine: mach_port_names_helper
113 * Purpose:
114 * A helper function for mach_port_names.
6d2010ae
A
115 *
116 * Conditions:
117 * Space containing entry is [at least] read-locked.
1c79356b 118 */
cb323159 119static void
1c79356b 120mach_port_names_helper(
0a7de745
A
121 ipc_port_timestamp_t timestamp,
122 ipc_entry_t entry,
123 mach_port_name_t name,
124 mach_port_name_t *names,
125 mach_port_type_t *types,
126 ipc_entry_num_t *actualp)
1c79356b
A
127{
128 ipc_entry_bits_t bits;
129 ipc_port_request_index_t request;
6d2010ae 130 mach_port_type_t type = 0;
1c79356b 131 ipc_entry_num_t actual;
6d2010ae 132 ipc_port_t port;
1c79356b
A
133
134 bits = entry->ie_bits;
135 request = entry->ie_request;
cb323159 136 port = ip_object_to_port(entry->ie_object);
1c79356b 137
6d2010ae
A
138 if (bits & MACH_PORT_TYPE_RECEIVE) {
139 assert(IP_VALID(port));
1c79356b 140
6d2010ae
A
141 if (request != IE_REQ_NONE) {
142 ip_lock(port);
cb323159 143 require_ip_active(port);
6d2010ae
A
144 type |= ipc_port_request_type(port, name, request);
145 ip_unlock(port);
146 }
6d2010ae
A
147 } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
148 mach_port_type_t reqtype;
1c79356b 149
6d2010ae
A
150 assert(IP_VALID(port));
151 ip_lock(port);
1c79356b 152
6d2010ae 153 reqtype = (request != IE_REQ_NONE) ?
0a7de745
A
154 ipc_port_request_type(port, name, request) : 0;
155
6d2010ae
A
156 /*
157 * If the port is alive, or was alive when the mach_port_names
158 * started, then return that fact. Otherwise, pretend we found
159 * a dead name entry.
160 */
161 if (ip_active(port) || IP_TIMESTAMP_ORDER(timestamp, port->ip_timestamp)) {
162 type |= reqtype;
163 } else {
1c79356b
A
164 bits &= ~(IE_BITS_TYPE_MASK);
165 bits |= MACH_PORT_TYPE_DEAD_NAME;
6d2010ae 166 /* account for additional reference for dead-name notification */
0a7de745 167 if (reqtype != 0) {
1c79356b 168 bits++;
0a7de745 169 }
1c79356b 170 }
6d2010ae 171 ip_unlock(port);
1c79356b
A
172 }
173
6d2010ae 174 type |= IE_BITS_TYPE(bits);
1c79356b
A
175
176 actual = *actualp;
177 names[actual] = name;
178 types[actual] = type;
0a7de745 179 *actualp = actual + 1;
1c79356b
A
180}
181
182/*
183 * Routine: mach_port_names [kernel call]
184 * Purpose:
185 * Retrieves a list of the rights present in the space,
186 * along with type information. (Same as returned
187 * by mach_port_type.) The names are returned in
188 * no particular order, but they (and the type info)
189 * are an accurate snapshot of the space.
190 * Conditions:
191 * Nothing locked.
192 * Returns:
193 * KERN_SUCCESS Arrays of names and types returned.
194 * KERN_INVALID_TASK The space is null.
195 * KERN_INVALID_TASK The space is dead.
196 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
197 */
198
199kern_return_t
200mach_port_names(
0a7de745
A
201 ipc_space_t space,
202 mach_port_name_t **namesp,
203 mach_msg_type_number_t *namesCnt,
204 mach_port_type_t **typesp,
205 mach_msg_type_number_t *typesCnt)
1c79356b 206{
1c79356b
A
207 ipc_entry_t table;
208 ipc_entry_num_t tsize;
209 mach_port_index_t index;
0a7de745
A
210 ipc_entry_num_t actual; /* this many names */
211 ipc_port_timestamp_t timestamp; /* logical time of this operation */
1c79356b
A
212 mach_port_name_t *names;
213 mach_port_type_t *types;
214 kern_return_t kr;
215
0a7de745
A
216 vm_size_t size; /* size of allocated memory */
217 vm_offset_t addr1; /* allocated memory, for names */
218 vm_offset_t addr2; /* allocated memory, for types */
219 vm_map_copy_t memory1; /* copied-in memory, for names */
220 vm_map_copy_t memory2; /* copied-in memory, for types */
1c79356b
A
221
222 /* safe simplifying assumption */
39037602 223 static_assert(sizeof(mach_port_name_t) == sizeof(mach_port_type_t));
1c79356b 224
0a7de745 225 if (space == IS_NULL) {
1c79356b 226 return KERN_INVALID_TASK;
0a7de745 227 }
1c79356b
A
228
229 size = 0;
230
231 for (;;) {
232 ipc_entry_num_t bound;
233 vm_size_t size_needed;
234
235 is_read_lock(space);
316670eb 236 if (!is_active(space)) {
1c79356b
A
237 is_read_unlock(space);
238 if (size != 0) {
239 kmem_free(ipc_kernel_map, addr1, size);
240 kmem_free(ipc_kernel_map, addr2, size);
241 }
242 return KERN_INVALID_TASK;
243 }
244
245 /* upper bound on number of names in the space */
316670eb 246 bound = space->is_table_size;
39236c6e
A
247 size_needed = vm_map_round_page(
248 (bound * sizeof(mach_port_name_t)),
249 VM_MAP_PAGE_MASK(ipc_kernel_map));
1c79356b 250
0a7de745 251 if (size_needed <= size) {
1c79356b 252 break;
0a7de745 253 }
1c79356b
A
254
255 is_read_unlock(space);
256
257 if (size != 0) {
258 kmem_free(ipc_kernel_map, addr1, size);
259 kmem_free(ipc_kernel_map, addr2, size);
260 }
261 size = size_needed;
262
5ba3f43e 263 kr = vm_allocate_kernel(ipc_kernel_map, &addr1, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC);
0a7de745 264 if (kr != KERN_SUCCESS) {
1c79356b 265 return KERN_RESOURCE_SHORTAGE;
0a7de745 266 }
1c79356b 267
5ba3f43e 268 kr = vm_allocate_kernel(ipc_kernel_map, &addr2, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC);
1c79356b
A
269 if (kr != KERN_SUCCESS) {
270 kmem_free(ipc_kernel_map, addr1, size);
271 return KERN_RESOURCE_SHORTAGE;
272 }
273
274 /* can't fault while we hold locks */
275
5ba3f43e 276 kr = vm_map_wire_kernel(
39236c6e
A
277 ipc_kernel_map,
278 vm_map_trunc_page(addr1,
0a7de745 279 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 280 vm_map_round_page(addr1 + size,
0a7de745
A
281 VM_MAP_PAGE_MASK(ipc_kernel_map)),
282 VM_PROT_READ | VM_PROT_WRITE, VM_KERN_MEMORY_IPC,
39236c6e 283 FALSE);
9bccf70c
A
284 if (kr != KERN_SUCCESS) {
285 kmem_free(ipc_kernel_map, addr1, size);
286 kmem_free(ipc_kernel_map, addr2, size);
287 return KERN_RESOURCE_SHORTAGE;
288 }
1c79356b 289
5ba3f43e 290 kr = vm_map_wire_kernel(
39236c6e
A
291 ipc_kernel_map,
292 vm_map_trunc_page(addr2,
0a7de745 293 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 294 vm_map_round_page(addr2 + size,
0a7de745
A
295 VM_MAP_PAGE_MASK(ipc_kernel_map)),
296 VM_PROT_READ | VM_PROT_WRITE,
5ba3f43e 297 VM_KERN_MEMORY_IPC,
39236c6e 298 FALSE);
9bccf70c
A
299 if (kr != KERN_SUCCESS) {
300 kmem_free(ipc_kernel_map, addr1, size);
301 kmem_free(ipc_kernel_map, addr2, size);
302 return KERN_RESOURCE_SHORTAGE;
303 }
1c79356b
A
304 }
305 /* space is read-locked and active */
306
307 names = (mach_port_name_t *) addr1;
308 types = (mach_port_type_t *) addr2;
309 actual = 0;
310
311 timestamp = ipc_port_timestamp();
312
313 table = space->is_table;
314 tsize = space->is_table_size;
315
316 for (index = 0; index < tsize; index++) {
317 ipc_entry_t entry = &table[index];
318 ipc_entry_bits_t bits = entry->ie_bits;
319
320 if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) {
321 mach_port_name_t name;
322
323 name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
324 mach_port_names_helper(timestamp, entry, name, names,
0a7de745 325 types, &actual);
1c79356b
A
326 }
327 }
328
1c79356b
A
329 is_read_unlock(space);
330
331 if (actual == 0) {
332 memory1 = VM_MAP_COPY_NULL;
333 memory2 = VM_MAP_COPY_NULL;
334
335 if (size != 0) {
336 kmem_free(ipc_kernel_map, addr1, size);
337 kmem_free(ipc_kernel_map, addr2, size);
338 }
339 } else {
340 vm_size_t size_used;
341 vm_size_t vm_size_used;
342
343 size_used = actual * sizeof(mach_port_name_t);
39236c6e 344 vm_size_used =
0a7de745
A
345 vm_map_round_page(size_used,
346 VM_MAP_PAGE_MASK(ipc_kernel_map));
1c79356b
A
347
348 /*
349 * Make used memory pageable and get it into
350 * copied-in form. Free any unused memory.
351 */
352
39236c6e
A
353 kr = vm_map_unwire(
354 ipc_kernel_map,
355 vm_map_trunc_page(addr1,
0a7de745 356 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 357 vm_map_round_page(addr1 + vm_size_used,
0a7de745 358 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 359 FALSE);
1c79356b
A
360 assert(kr == KERN_SUCCESS);
361
39236c6e
A
362 kr = vm_map_unwire(
363 ipc_kernel_map,
364 vm_map_trunc_page(addr2,
0a7de745 365 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 366 vm_map_round_page(addr2 + vm_size_used,
0a7de745 367 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 368 FALSE);
1c79356b
A
369 assert(kr == KERN_SUCCESS);
370
91447636 371 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr1,
0a7de745 372 (vm_map_size_t)size_used, TRUE, &memory1);
1c79356b
A
373 assert(kr == KERN_SUCCESS);
374
91447636 375 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr2,
0a7de745 376 (vm_map_size_t)size_used, TRUE, &memory2);
1c79356b
A
377 assert(kr == KERN_SUCCESS);
378
379 if (vm_size_used != size) {
380 kmem_free(ipc_kernel_map,
0a7de745 381 addr1 + vm_size_used, size - vm_size_used);
1c79356b 382 kmem_free(ipc_kernel_map,
0a7de745 383 addr2 + vm_size_used, size - vm_size_used);
1c79356b
A
384 }
385 }
386
387 *namesp = (mach_port_name_t *) memory1;
388 *namesCnt = actual;
389 *typesp = (mach_port_type_t *) memory2;
390 *typesCnt = actual;
391 return KERN_SUCCESS;
392}
393
394/*
395 * Routine: mach_port_type [kernel call]
396 * Purpose:
397 * Retrieves the type of a right in the space.
398 * The type is a bitwise combination of one or more
399 * of the following type bits:
400 * MACH_PORT_TYPE_SEND
401 * MACH_PORT_TYPE_RECEIVE
402 * MACH_PORT_TYPE_SEND_ONCE
403 * MACH_PORT_TYPE_PORT_SET
404 * MACH_PORT_TYPE_DEAD_NAME
405 * In addition, the following pseudo-type bits may be present:
406 * MACH_PORT_TYPE_DNREQUEST
407 * A dead-name notification is requested.
408 * Conditions:
409 * Nothing locked.
410 * Returns:
411 * KERN_SUCCESS Type is returned.
412 * KERN_INVALID_TASK The space is null.
413 * KERN_INVALID_TASK The space is dead.
414 * KERN_INVALID_NAME The name doesn't denote a right.
415 */
416
417kern_return_t
418mach_port_type(
0a7de745
A
419 ipc_space_t space,
420 mach_port_name_t name,
421 mach_port_type_t *typep)
1c79356b
A
422{
423 mach_port_urefs_t urefs;
424 ipc_entry_t entry;
425 kern_return_t kr;
426
0a7de745 427 if (space == IS_NULL) {
1c79356b 428 return KERN_INVALID_TASK;
0a7de745 429 }
1c79356b 430
0a7de745 431 if (name == MACH_PORT_NULL) {
1c79356b 432 return KERN_INVALID_NAME;
0a7de745 433 }
1c79356b
A
434
435 if (name == MACH_PORT_DEAD) {
436 *typep = MACH_PORT_TYPE_DEAD_NAME;
437 return KERN_SUCCESS;
438 }
439
440 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523 441 if (kr != KERN_SUCCESS) {
1c79356b 442 return kr;
d9a64523 443 }
1c79356b 444
316670eb 445 /* space is write-locked and active */
1c79356b 446 kr = ipc_right_info(space, name, entry, typep, &urefs);
316670eb
A
447 /* space is unlocked */
448
6d2010ae 449#if 1
0a7de745
A
450 /* JMM - workaround rdar://problem/9121297 (CF being too picky on these bits). */
451 *typep &= ~(MACH_PORT_TYPE_SPREQUEST | MACH_PORT_TYPE_SPREQUEST_DELAYED);
6d2010ae
A
452#endif
453
1c79356b
A
454 return kr;
455}
456
457/*
458 * Routine: mach_port_rename [kernel call]
459 * Purpose:
460 * Changes the name denoting a right,
461 * from oname to nname.
462 * Conditions:
463 * Nothing locked.
464 * Returns:
465 * KERN_SUCCESS The right is renamed.
466 * KERN_INVALID_TASK The space is null.
467 * KERN_INVALID_TASK The space is dead.
468 * KERN_INVALID_NAME The oname doesn't denote a right.
469 * KERN_INVALID_VALUE The nname isn't a legal name.
470 * KERN_NAME_EXISTS The nname already denotes a right.
471 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
316670eb
A
472 *
473 * This interface is obsolete and always returns
474 * KERN_NOT_SUPPORTED.
1c79356b
A
475 */
476
477kern_return_t
478mach_port_rename(
0a7de745
A
479 __unused ipc_space_t space,
480 __unused mach_port_name_t oname,
481 __unused mach_port_name_t nname)
1c79356b 482{
316670eb 483 return KERN_NOT_SUPPORTED;
1c79356b
A
484}
485
316670eb 486
1c79356b
A
487/*
488 * Routine: mach_port_allocate_name [kernel call]
489 * Purpose:
490 * Allocates a right in a space, using a specific name
491 * for the new right. Possible rights:
492 * MACH_PORT_RIGHT_RECEIVE
493 * MACH_PORT_RIGHT_PORT_SET
494 * MACH_PORT_RIGHT_DEAD_NAME
495 *
496 * A new port (allocated with MACH_PORT_RIGHT_RECEIVE)
497 * has no extant send or send-once rights and no queued
498 * messages. Its queue limit is MACH_PORT_QLIMIT_DEFAULT
499 * and its make-send count is 0. It is not a member of
500 * a port set. It has no registered no-senders or
501 * port-destroyed notification requests.
502 *
503 * A new port set has no members.
504 *
505 * A new dead name has one user reference.
506 * Conditions:
507 * Nothing locked.
508 * Returns:
509 * KERN_SUCCESS The right is allocated.
510 * KERN_INVALID_TASK The space is null.
511 * KERN_INVALID_TASK The space is dead.
512 * KERN_INVALID_VALUE The name isn't a legal name.
513 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
514 * KERN_NAME_EXISTS The name already denotes a right.
515 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
516 *
517 * Restrictions on name allocation: NT bits are reserved by kernel,
518 * must be set on any chosen name. Can't do this at all in kernel
519 * loaded server.
520 */
521
522kern_return_t
523mach_port_allocate_name(
0a7de745
A
524 ipc_space_t space,
525 mach_port_right_t right,
526 mach_port_name_t name)
1c79356b 527{
0a7de745
A
528 kern_return_t kr;
529 mach_port_qos_t qos = qos_template;
1c79356b
A
530
531 qos.name = TRUE;
532
0a7de745 533 if (!MACH_PORT_VALID(name)) {
1c79356b 534 return KERN_INVALID_VALUE;
0a7de745 535 }
1c79356b 536
0a7de745
A
537 kr = mach_port_allocate_full(space, right, MACH_PORT_NULL,
538 &qos, &name);
539 return kr;
1c79356b
A
540}
541
542/*
543 * Routine: mach_port_allocate [kernel call]
544 * Purpose:
545 * Allocates a right in a space. Like mach_port_allocate_name,
546 * except that the implementation picks a name for the right.
547 * The name may be any legal name in the space that doesn't
548 * currently denote a right.
549 * Conditions:
550 * Nothing locked.
551 * Returns:
552 * KERN_SUCCESS The right is allocated.
553 * KERN_INVALID_TASK The space is null.
554 * KERN_INVALID_TASK The space is dead.
555 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
556 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
557 * KERN_NO_SPACE No room in space for another right.
558 */
559
560kern_return_t
561mach_port_allocate(
0a7de745
A
562 ipc_space_t space,
563 mach_port_right_t right,
564 mach_port_name_t *namep)
1c79356b 565{
0a7de745
A
566 kern_return_t kr;
567 mach_port_qos_t qos = qos_template;
1c79356b 568
0a7de745
A
569 kr = mach_port_allocate_full(space, right, MACH_PORT_NULL,
570 &qos, namep);
571 return kr;
1c79356b
A
572}
573
574/*
575 * Routine: mach_port_allocate_qos [kernel call]
576 * Purpose:
0a7de745
A
577 * Allocates a right, with qos options, in a space. Like
578 * mach_port_allocate_name, except that the implementation
579 * picks a name for the right. The name may be any legal name
1c79356b
A
580 * in the space that doesn't currently denote a right.
581 * Conditions:
582 * Nothing locked.
583 * Returns:
584 * KERN_SUCCESS The right is allocated.
585 * KERN_INVALID_TASK The space is null.
586 * KERN_INVALID_TASK The space is dead.
587 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
588 * KERN_INVALID_ARGUMENT The qos request was invalid.
589 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
590 * KERN_NO_SPACE No room in space for another right.
591 */
592
593kern_return_t
594mach_port_allocate_qos(
0a7de745
A
595 ipc_space_t space,
596 mach_port_right_t right,
597 mach_port_qos_t *qosp,
598 mach_port_name_t *namep)
1c79356b 599{
0a7de745 600 kern_return_t kr;
1c79356b 601
0a7de745 602 if (qosp->name) {
1c79356b 603 return KERN_INVALID_ARGUMENT;
0a7de745
A
604 }
605 kr = mach_port_allocate_full(space, right, MACH_PORT_NULL,
606 qosp, namep);
607 return kr;
1c79356b
A
608}
609
1c79356b
A
610/*
611 * Routine: mach_port_allocate_full [kernel call]
612 * Purpose:
cb323159
A
613 * Allocates a right in a space. Supports the
614 * special case of specifying a name. The name may
615 * be any legal name in the space that doesn't
1c79356b 616 * currently denote a right.
cb323159
A
617 *
618 * While we no longer support users requesting
619 * preallocated message for the port, we still
620 * check for errors in such requests and then
621 * just clear the request.
1c79356b
A
622 * Conditions:
623 * Nothing locked.
624 * Returns:
625 * KERN_SUCCESS The right is allocated.
626 * KERN_INVALID_TASK The space is null.
627 * KERN_INVALID_TASK The space is dead.
628 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
629 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
630 * KERN_NO_SPACE No room in space for another right.
631 */
632
633kern_return_t
634mach_port_allocate_full(
0a7de745
A
635 ipc_space_t space,
636 mach_port_right_t right,
637 mach_port_t proto,
638 mach_port_qos_t *qosp,
639 mach_port_name_t *namep)
1c79356b 640{
0a7de745 641 kern_return_t kr;
1c79356b 642
0a7de745
A
643 if (space == IS_NULL) {
644 return KERN_INVALID_TASK;
645 }
1c79356b 646
0a7de745
A
647 if (proto != MACH_PORT_NULL) {
648 return KERN_INVALID_VALUE;
649 }
9bccf70c 650
1c79356b 651 if (qosp->name) {
0a7de745
A
652 if (!MACH_PORT_VALID(*namep)) {
653 return KERN_INVALID_VALUE;
654 }
1c79356b
A
655 }
656
cb323159
A
657 /*
658 * Don't actually honor prealloc requests from user-space
659 * (for security reasons, and because it isn't guaranteed anyway).
660 * Keep old errors for legacy reasons.
661 */
1c79356b 662 if (qosp->prealloc) {
8ad349bb
A
663 if (qosp->len > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE) {
664 return KERN_RESOURCE_SHORTAGE;
8ad349bb 665 }
cb323159
A
666 if (right != MACH_PORT_RIGHT_RECEIVE) {
667 return KERN_INVALID_VALUE;
668 }
669 qosp->prealloc = 0;
1c79356b
A
670 }
671
cb323159
A
672 kr = mach_port_allocate_internal(space, right, qosp, namep);
673 return kr;
674}
675
676
677/*
678 * Routine: mach_port_allocate_internal [kernel private]
679 * Purpose:
680 * Allocates a right in a space. Supports all of the
681 * special cases, a specific name, a real-time port, etc.
682 * The name may be any legal name in the space that doesn't
683 * currently denote a right.
684 * Conditions:
685 * Nothing locked.
686 * Returns:
687 * KERN_SUCCESS The right is allocated.
688 * KERN_INVALID_TASK The space is null.
689 * KERN_INVALID_TASK The space is dead.
690 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
691 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
692 * KERN_NO_SPACE No room in space for another right.
693 */
694kern_return_t
695mach_port_allocate_internal(
696 ipc_space_t space,
697 mach_port_right_t right,
698 mach_port_qos_t *qosp,
699 mach_port_name_t *namep)
700{
701 kern_return_t kr;
702
703 assert(space != IS_NULL);
704
1c79356b 705 switch (right) {
0a7de745
A
706 case MACH_PORT_RIGHT_RECEIVE:
707 {
cb323159 708 ipc_kmsg_t kmsg = IKM_NULL;
0a7de745 709 ipc_port_t port;
1c79356b 710
cb323159
A
711 /*
712 * For in-kernel uses, only allow small (from the kmsg zone)
713 * preallocated messages for the port.
714 */
715 if (qosp->prealloc) {
716 mach_msg_size_t size = qosp->len;
717
718 if (size > IKM_SAVED_MSG_SIZE - MAX_TRAILER_SIZE) {
719 panic("mach_port_allocate_internal: too large a prealloc kmsg");
720 }
721 kmsg = (ipc_kmsg_t)ipc_kmsg_prealloc(size + MAX_TRAILER_SIZE);
722 if (kmsg == IKM_NULL) {
723 return KERN_RESOURCE_SHORTAGE;
724 }
725 }
726
0a7de745 727 if (qosp->name) {
94ff46dc
A
728 kr = ipc_port_alloc_name(space, IPC_PORT_INIT_MESSAGE_QUEUE,
729 *namep, &port);
0a7de745 730 } else {
94ff46dc
A
731 kr = ipc_port_alloc(space, IPC_PORT_INIT_MESSAGE_QUEUE,
732 namep, &port);
0a7de745 733 }
1c79356b 734 if (kr == KERN_SUCCESS) {
0a7de745 735 if (kmsg != IKM_NULL) {
1c79356b 736 ipc_kmsg_set_prealloc(kmsg, port);
0a7de745 737 }
1c79356b 738 ip_unlock(port);
0a7de745 739 } else if (kmsg != IKM_NULL) {
1c79356b 740 ipc_kmsg_free(kmsg);
0a7de745 741 }
1c79356b 742 break;
0a7de745 743 }
1c79356b 744
0a7de745
A
745 case MACH_PORT_RIGHT_PORT_SET:
746 {
747 ipc_pset_t pset;
1c79356b 748
0a7de745 749 if (qosp->name) {
1c79356b 750 kr = ipc_pset_alloc_name(space, *namep, &pset);
0a7de745 751 } else {
1c79356b 752 kr = ipc_pset_alloc(space, namep, &pset);
0a7de745
A
753 }
754 if (kr == KERN_SUCCESS) {
1c79356b 755 ips_unlock(pset);
0a7de745 756 }
1c79356b 757 break;
0a7de745 758 }
1c79356b 759
0a7de745 760 case MACH_PORT_RIGHT_DEAD_NAME:
1c79356b
A
761 kr = ipc_object_alloc_dead(space, namep);
762 break;
763
0a7de745 764 default:
1c79356b
A
765 kr = KERN_INVALID_VALUE;
766 break;
767 }
768
0a7de745 769 return kr;
1c79356b
A
770}
771
772/*
773 * Routine: mach_port_destroy [kernel call]
774 * Purpose:
775 * Cleans up and destroys all rights denoted by a name
776 * in a space. The destruction of a receive right
777 * destroys the port, unless a port-destroyed request
778 * has been made for it; the destruction of a port-set right
779 * destroys the port set.
780 * Conditions:
781 * Nothing locked.
782 * Returns:
783 * KERN_SUCCESS The name is destroyed.
784 * KERN_INVALID_TASK The space is null.
785 * KERN_INVALID_TASK The space is dead.
786 * KERN_INVALID_NAME The name doesn't denote a right.
787 */
788
789kern_return_t
790mach_port_destroy(
0a7de745
A
791 ipc_space_t space,
792 mach_port_name_t name)
1c79356b
A
793{
794 ipc_entry_t entry;
795 kern_return_t kr;
796
0a7de745 797 if (space == IS_NULL) {
1c79356b 798 return KERN_INVALID_TASK;
0a7de745 799 }
1c79356b 800
0a7de745 801 if (!MACH_PORT_VALID(name)) {
1c79356b 802 return KERN_SUCCESS;
0a7de745 803 }
1c79356b
A
804
805 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
806 if (kr != KERN_SUCCESS) {
807 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b 808 return kr;
d9a64523 809 }
1c79356b
A
810 /* space is write-locked and active */
811
39236c6e 812 kr = ipc_right_destroy(space, name, entry, TRUE, 0); /* unlocks space */
1c79356b
A
813 return kr;
814}
815
816/*
817 * Routine: mach_port_deallocate [kernel call]
818 * Purpose:
819 * Deallocates a user reference from a send right,
5ba3f43e
A
820 * send-once right, dead-name right or a port_set right.
821 * May deallocate the right, if this is the last uref,
1c79356b
A
822 * and destroy the name, if it doesn't denote
823 * other rights.
824 * Conditions:
825 * Nothing locked.
826 * Returns:
827 * KERN_SUCCESS The uref is deallocated.
828 * KERN_INVALID_TASK The space is null.
829 * KERN_INVALID_TASK The space is dead.
830 * KERN_INVALID_NAME The name doesn't denote a right.
831 * KERN_INVALID_RIGHT The right isn't correct.
832 */
833
834kern_return_t
835mach_port_deallocate(
0a7de745
A
836 ipc_space_t space,
837 mach_port_name_t name)
1c79356b
A
838{
839 ipc_entry_t entry;
840 kern_return_t kr;
841
0a7de745 842 if (space == IS_NULL) {
1c79356b 843 return KERN_INVALID_TASK;
0a7de745 844 }
1c79356b 845
0a7de745 846 if (!MACH_PORT_VALID(name)) {
1c79356b 847 return KERN_SUCCESS;
0a7de745 848 }
1c79356b
A
849
850 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
851 if (kr != KERN_SUCCESS) {
852 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b 853 return kr;
d9a64523 854 }
1c79356b
A
855 /* space is write-locked */
856
857 kr = ipc_right_dealloc(space, name, entry); /* unlocks space */
858 return kr;
859}
860
861/*
862 * Routine: mach_port_get_refs [kernel call]
863 * Purpose:
864 * Retrieves the number of user references held by a right.
865 * Receive rights, port-set rights, and send-once rights
866 * always have one user reference. Returns zero if the
867 * name denotes a right, but not the queried right.
868 * Conditions:
869 * Nothing locked.
870 * Returns:
871 * KERN_SUCCESS Number of urefs returned.
872 * KERN_INVALID_TASK The space is null.
873 * KERN_INVALID_TASK The space is dead.
874 * KERN_INVALID_VALUE "right" isn't a legal value.
875 * KERN_INVALID_NAME The name doesn't denote a right.
876 */
877
878kern_return_t
879mach_port_get_refs(
0a7de745
A
880 ipc_space_t space,
881 mach_port_name_t name,
882 mach_port_right_t right,
883 mach_port_urefs_t *urefsp)
1c79356b
A
884{
885 mach_port_type_t type;
886 mach_port_urefs_t urefs;
887 ipc_entry_t entry;
888 kern_return_t kr;
889
0a7de745 890 if (space == IS_NULL) {
1c79356b 891 return KERN_INVALID_TASK;
0a7de745 892 }
1c79356b 893
0a7de745 894 if (right >= MACH_PORT_RIGHT_NUMBER) {
1c79356b 895 return KERN_INVALID_VALUE;
0a7de745 896 }
1c79356b
A
897
898 if (!MACH_PORT_VALID(name)) {
0a7de745 899 if (right == MACH_PORT_RIGHT_SEND ||
1c79356b
A
900 right == MACH_PORT_RIGHT_SEND_ONCE) {
901 *urefsp = 1;
902 return KERN_SUCCESS;
903 }
904 return KERN_INVALID_NAME;
905 }
906
907 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523 908 if (kr != KERN_SUCCESS) {
1c79356b 909 return kr;
d9a64523 910 }
316670eb 911
1c79356b 912 /* space is write-locked and active */
316670eb
A
913 kr = ipc_right_info(space, name, entry, &type, &urefs);
914 /* space is unlocked */
1c79356b 915
0a7de745
A
916 if (kr != KERN_SUCCESS) {
917 return kr;
918 }
1c79356b 919
0a7de745 920 if (type & MACH_PORT_TYPE(right)) {
1c79356b 921 switch (right) {
0a7de745 922 case MACH_PORT_RIGHT_SEND_ONCE:
1c79356b 923 assert(urefs == 1);
0a7de745 924 /* fall-through */
1c79356b 925
0a7de745
A
926 case MACH_PORT_RIGHT_PORT_SET:
927 case MACH_PORT_RIGHT_RECEIVE:
1c79356b
A
928 *urefsp = 1;
929 break;
930
0a7de745
A
931 case MACH_PORT_RIGHT_DEAD_NAME:
932 case MACH_PORT_RIGHT_SEND:
1c79356b
A
933 assert(urefs > 0);
934 *urefsp = urefs;
935 break;
936
0a7de745 937 default:
1c79356b
A
938 panic("mach_port_get_refs: strange rights");
939 }
0a7de745 940 } else {
1c79356b 941 *urefsp = 0;
0a7de745 942 }
1c79356b
A
943
944 return kr;
945}
946
947/*
948 * Routine: mach_port_mod_refs
949 * Purpose:
950 * Modifies the number of user references held by a right.
951 * The resulting number of user references must be non-negative.
952 * If it is zero, the right is deallocated. If the name
953 * doesn't denote other rights, it is destroyed.
954 * Conditions:
955 * Nothing locked.
956 * Returns:
957 * KERN_SUCCESS Modified number of urefs.
958 * KERN_INVALID_TASK The space is null.
959 * KERN_INVALID_TASK The space is dead.
960 * KERN_INVALID_VALUE "right" isn't a legal value.
961 * KERN_INVALID_NAME The name doesn't denote a right.
962 * KERN_INVALID_RIGHT Name doesn't denote specified right.
963 * KERN_INVALID_VALUE Impossible modification to urefs.
964 * KERN_UREFS_OVERFLOW Urefs would overflow.
965 */
966
967kern_return_t
968mach_port_mod_refs(
0a7de745
A
969 ipc_space_t space,
970 mach_port_name_t name,
971 mach_port_right_t right,
972 mach_port_delta_t delta)
1c79356b
A
973{
974 ipc_entry_t entry;
975 kern_return_t kr;
976
0a7de745 977 if (space == IS_NULL) {
1c79356b 978 return KERN_INVALID_TASK;
0a7de745 979 }
1c79356b 980
0a7de745 981 if (right >= MACH_PORT_RIGHT_NUMBER) {
1c79356b 982 return KERN_INVALID_VALUE;
0a7de745 983 }
1c79356b
A
984
985 if (!MACH_PORT_VALID(name)) {
986 if (right == MACH_PORT_RIGHT_SEND ||
0a7de745 987 right == MACH_PORT_RIGHT_SEND_ONCE) {
1c79356b 988 return KERN_SUCCESS;
0a7de745 989 }
1c79356b
A
990 return KERN_INVALID_NAME;
991 }
992
993 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
994 if (kr != KERN_SUCCESS) {
995 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b 996 return kr;
d9a64523
A
997 }
998
1c79356b
A
999 /* space is write-locked and active */
1000
0a7de745 1001 kr = ipc_right_delta(space, name, entry, right, delta); /* unlocks */
1c79356b
A
1002 return kr;
1003}
1004
1005
39236c6e
A
1006/*
1007 * Routine: mach_port_peek [kernel call]
1008 * Purpose:
1009 * Peek at the message queue for the specified receive
1010 * right and return info about a message in the queue.
1011 *
1012 * On input, seqnop points to a sequence number value
1013 * to match the message being peeked. If zero is specified
1014 * as the seqno, the first message in the queue will be
1015 * peeked.
1016 *
1017 * Only the following trailer types are currently supported:
1018 * MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)
1019 *
1020 * or'ed with one of these element types:
1021 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_NULL)
1022 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SEQNO)
1023 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)
1024 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)
1025 *
1026 * On input, the value pointed to by trailer_sizep must be
1027 * large enough to hold the requested trailer size.
1028 *
1029 * The message sequence number, id, size, requested trailer info
1030 * and requested trailer size are returned in their respective
1031 * output parameters upon success.
1032 *
1033 * Conditions:
1034 * Nothing locked.
1035 * Returns:
1036 * KERN_SUCCESS Matching message found, out parameters set.
1037 * KERN_INVALID_TASK The space is null or dead.
1038 * KERN_INVALID_NAME The name doesn't denote a right.
1039 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1040 * KERN_INVALID_VALUE The input parameter values are out of bounds.
1041 * KERN_FAILURE The requested message was not found.
1042 */
1043
1044kern_return_t
1045mach_port_peek(
0a7de745
A
1046 ipc_space_t space,
1047 mach_port_name_t name,
1048 mach_msg_trailer_type_t trailer_type,
1049 mach_port_seqno_t *seqnop,
1050 mach_msg_size_t *msg_sizep,
1051 mach_msg_id_t *msg_idp,
1052 mach_msg_trailer_info_t trailer_infop,
1053 mach_msg_type_number_t *trailer_sizep)
39236c6e
A
1054{
1055 ipc_port_t port;
1056 kern_return_t kr;
1057 boolean_t found;
1058 mach_msg_max_trailer_t max_trailer;
1059
0a7de745 1060 if (space == IS_NULL) {
39236c6e 1061 return KERN_INVALID_TASK;
0a7de745 1062 }
39236c6e 1063
0a7de745 1064 if (!MACH_PORT_VALID(name)) {
39236c6e 1065 return KERN_INVALID_RIGHT;
0a7de745 1066 }
39236c6e
A
1067
1068 /*
1069 * We don't allow anything greater than the audit trailer - to avoid
1070 * leaking the context pointer and to avoid variable-sized context issues.
1071 */
1072 if (GET_RCV_ELEMENTS(trailer_type) > MACH_RCV_TRAILER_AUDIT ||
d9a64523
A
1073 REQUESTED_TRAILER_SIZE(TRUE, trailer_type) > *trailer_sizep) {
1074 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE);
39236c6e 1075 return KERN_INVALID_VALUE;
d9a64523 1076 }
39236c6e
A
1077
1078 *trailer_sizep = REQUESTED_TRAILER_SIZE(TRUE, trailer_type);
1079
1080 kr = ipc_port_translate_receive(space, name, &port);
d9a64523
A
1081 if (kr != KERN_SUCCESS) {
1082 mach_port_guard_exception(name, 0, 0,
0a7de745
A
1083 ((KERN_INVALID_NAME == kr) ?
1084 kGUARD_EXC_INVALID_NAME :
1085 kGUARD_EXC_INVALID_RIGHT));
39236c6e 1086 return kr;
d9a64523 1087 }
39236c6e
A
1088
1089 /* Port locked and active */
1090
1091 found = ipc_mqueue_peek(&port->ip_messages, seqnop,
0a7de745 1092 msg_sizep, msg_idp, &max_trailer, NULL);
39236c6e
A
1093 ip_unlock(port);
1094
0a7de745 1095 if (found != TRUE) {
39236c6e 1096 return KERN_FAILURE;
0a7de745 1097 }
39236c6e
A
1098
1099 max_trailer.msgh_seqno = *seqnop;
1100 memcpy(trailer_infop, &max_trailer, *trailer_sizep);
1101
1102 return KERN_SUCCESS;
1103}
1104
1c79356b
A
1105/*
1106 * Routine: mach_port_set_mscount [kernel call]
1107 * Purpose:
1108 * Changes a receive right's make-send count.
1109 * Conditions:
1110 * Nothing locked.
1111 * Returns:
1112 * KERN_SUCCESS Set make-send count.
1113 * KERN_INVALID_TASK The space is null.
1114 * KERN_INVALID_TASK The space is dead.
1115 * KERN_INVALID_NAME The name doesn't denote a right.
1116 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1117 */
1118
1119kern_return_t
1120mach_port_set_mscount(
0a7de745
A
1121 ipc_space_t space,
1122 mach_port_name_t name,
1123 mach_port_mscount_t mscount)
1c79356b
A
1124{
1125 ipc_port_t port;
1126 kern_return_t kr;
1127
0a7de745 1128 if (space == IS_NULL) {
1c79356b 1129 return KERN_INVALID_TASK;
0a7de745 1130 }
1c79356b 1131
0a7de745 1132 if (!MACH_PORT_VALID(name)) {
1c79356b 1133 return KERN_INVALID_RIGHT;
0a7de745 1134 }
1c79356b
A
1135
1136 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1137 if (kr != KERN_SUCCESS) {
1c79356b 1138 return kr;
0a7de745 1139 }
1c79356b
A
1140 /* port is locked and active */
1141
cb323159 1142 port->ip_mscount = mscount;
1c79356b
A
1143 ip_unlock(port);
1144 return KERN_SUCCESS;
1145}
1146
1147/*
1148 * Routine: mach_port_set_seqno [kernel call]
1149 * Purpose:
1150 * Changes a receive right's sequence number.
1151 * Conditions:
1152 * Nothing locked.
1153 * Returns:
1154 * KERN_SUCCESS Set sequence number.
1155 * KERN_INVALID_TASK The space is null.
1156 * KERN_INVALID_TASK The space is dead.
1157 * KERN_INVALID_NAME The name doesn't denote a right.
1158 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1159 */
1160
1161kern_return_t
1162mach_port_set_seqno(
0a7de745
A
1163 ipc_space_t space,
1164 mach_port_name_t name,
1165 mach_port_seqno_t seqno)
1c79356b
A
1166{
1167 ipc_port_t port;
1168 kern_return_t kr;
1169
0a7de745 1170 if (space == IS_NULL) {
1c79356b 1171 return KERN_INVALID_TASK;
0a7de745 1172 }
1c79356b 1173
0a7de745 1174 if (!MACH_PORT_VALID(name)) {
1c79356b 1175 return KERN_INVALID_RIGHT;
0a7de745 1176 }
1c79356b
A
1177
1178 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1179 if (kr != KERN_SUCCESS) {
1c79356b 1180 return kr;
0a7de745 1181 }
1c79356b
A
1182 /* port is locked and active */
1183
1184 ipc_mqueue_set_seqno(&port->ip_messages, seqno);
1185
1186 ip_unlock(port);
1187 return KERN_SUCCESS;
1188}
1189
b0d623f7
A
1190/*
1191 * Routine: mach_port_get_context [kernel call]
1192 * Purpose:
1193 * Returns a receive right's context pointer.
1194 * Conditions:
1195 * Nothing locked.
1196 * Returns:
1197 * KERN_SUCCESS Set context pointer.
1198 * KERN_INVALID_TASK The space is null.
1199 * KERN_INVALID_TASK The space is dead.
1200 * KERN_INVALID_NAME The name doesn't denote a right.
1201 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1202 */
1203
1204kern_return_t
1205mach_port_get_context(
0a7de745
A
1206 ipc_space_t space,
1207 mach_port_name_t name,
1208 mach_vm_address_t *context)
b0d623f7
A
1209{
1210 ipc_port_t port;
1211 kern_return_t kr;
1212
0a7de745 1213 if (space == IS_NULL) {
b0d623f7 1214 return KERN_INVALID_TASK;
0a7de745 1215 }
b0d623f7 1216
0a7de745 1217 if (!MACH_PORT_VALID(name)) {
b0d623f7 1218 return KERN_INVALID_RIGHT;
0a7de745 1219 }
b0d623f7
A
1220
1221 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1222 if (kr != KERN_SUCCESS) {
b0d623f7 1223 return kr;
0a7de745 1224 }
b0d623f7 1225
39236c6e
A
1226 /* Port locked and active */
1227
1228 /* For strictly guarded ports, return empty context (which acts as guard) */
0a7de745 1229 if (port->ip_strict_guard) {
39236c6e 1230 *context = 0;
0a7de745 1231 } else {
39236c6e 1232 *context = port->ip_context;
0a7de745 1233 }
b0d623f7
A
1234
1235 ip_unlock(port);
1236 return KERN_SUCCESS;
1237}
1238
1239
1240/*
1241 * Routine: mach_port_set_context [kernel call]
1242 * Purpose:
1243 * Changes a receive right's context pointer.
1244 * Conditions:
1245 * Nothing locked.
1246 * Returns:
1247 * KERN_SUCCESS Set context pointer.
1248 * KERN_INVALID_TASK The space is null.
1249 * KERN_INVALID_TASK The space is dead.
1250 * KERN_INVALID_NAME The name doesn't denote a right.
1251 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1252 */
1253
1254kern_return_t
1255mach_port_set_context(
0a7de745
A
1256 ipc_space_t space,
1257 mach_port_name_t name,
1258 mach_vm_address_t context)
b0d623f7
A
1259{
1260 ipc_port_t port;
1261 kern_return_t kr;
1262
0a7de745 1263 if (space == IS_NULL) {
b0d623f7 1264 return KERN_INVALID_TASK;
0a7de745 1265 }
b0d623f7 1266
0a7de745 1267 if (!MACH_PORT_VALID(name)) {
b0d623f7 1268 return KERN_INVALID_RIGHT;
0a7de745 1269 }
b0d623f7
A
1270
1271 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1272 if (kr != KERN_SUCCESS) {
b0d623f7 1273 return kr;
0a7de745 1274 }
b0d623f7
A
1275
1276 /* port is locked and active */
0a7de745 1277 if (port->ip_strict_guard) {
39236c6e
A
1278 uint64_t portguard = port->ip_context;
1279 ip_unlock(port);
1280 /* For strictly guarded ports, disallow overwriting context; Raise Exception */
1281 mach_port_guard_exception(name, context, portguard, kGUARD_EXC_SET_CONTEXT);
1282 return KERN_INVALID_ARGUMENT;
1283 }
1284
b0d623f7
A
1285 port->ip_context = context;
1286
1287 ip_unlock(port);
1288 return KERN_SUCCESS;
1289}
1290
1291
1c79356b
A
1292/*
1293 * Routine: mach_port_get_set_status [kernel call]
1294 * Purpose:
1295 * Retrieves a list of members in a port set.
1296 * Returns the space's name for each receive right member.
1297 * Conditions:
1298 * Nothing locked.
1299 * Returns:
1300 * KERN_SUCCESS Retrieved list of members.
1301 * KERN_INVALID_TASK The space is null.
1302 * KERN_INVALID_TASK The space is dead.
1303 * KERN_INVALID_NAME The name doesn't denote a right.
1304 * KERN_INVALID_RIGHT Name doesn't denote a port set.
1305 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1306 */
1307
1308kern_return_t
1309mach_port_get_set_status(
0a7de745
A
1310 ipc_space_t space,
1311 mach_port_name_t name,
1312 mach_port_name_t **members,
1313 mach_msg_type_number_t *membersCnt)
1c79356b 1314{
0a7de745
A
1315 ipc_entry_num_t actual; /* this many members */
1316 ipc_entry_num_t maxnames; /* space for this many members */
1c79356b
A
1317 kern_return_t kr;
1318
0a7de745
A
1319 vm_size_t size; /* size of allocated memory */
1320 vm_offset_t addr; /* allocated memory */
1321 vm_map_copy_t memory; /* copied-in memory */
1c79356b 1322
0a7de745 1323 if (space == IS_NULL) {
1c79356b 1324 return KERN_INVALID_TASK;
0a7de745 1325 }
1c79356b 1326
0a7de745 1327 if (!MACH_PORT_VALID(name)) {
1c79356b 1328 return KERN_INVALID_RIGHT;
0a7de745 1329 }
1c79356b 1330
0a7de745 1331 size = VM_MAP_PAGE_SIZE(ipc_kernel_map); /* initial guess */
1c79356b
A
1332
1333 for (;;) {
1c79356b 1334 mach_port_name_t *names;
39236c6e 1335 ipc_object_t psobj;
1c79356b
A
1336 ipc_pset_t pset;
1337
5ba3f43e 1338 kr = vm_allocate_kernel(ipc_kernel_map, &addr, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC);
0a7de745 1339 if (kr != KERN_SUCCESS) {
1c79356b 1340 return KERN_RESOURCE_SHORTAGE;
0a7de745 1341 }
1c79356b
A
1342
1343 /* can't fault while we hold locks */
1344
5ba3f43e 1345 kr = vm_map_wire_kernel(ipc_kernel_map, addr, addr + size,
0a7de745 1346 VM_PROT_READ | VM_PROT_WRITE, VM_KERN_MEMORY_IPC, FALSE);
1c79356b
A
1347 assert(kr == KERN_SUCCESS);
1348
39236c6e 1349 kr = ipc_object_translate(space, name, MACH_PORT_RIGHT_PORT_SET, &psobj);
1c79356b
A
1350 if (kr != KERN_SUCCESS) {
1351 kmem_free(ipc_kernel_map, addr, size);
1352 return kr;
1353 }
1c79356b 1354
39236c6e 1355 /* just use a portset reference from here on out */
cb323159 1356 pset = ips_object_to_pset(psobj);
39236c6e 1357 ips_reference(pset);
0a7de745 1358 ips_unlock(pset);
1c79356b
A
1359
1360 names = (mach_port_name_t *) addr;
b0d623f7 1361 maxnames = (ipc_entry_num_t)(size / sizeof(mach_port_name_t));
1c79356b 1362
3e170ce0 1363 ipc_mqueue_set_gather_member_names(space, &pset->ips_messages, maxnames, names, &actual);
1c79356b 1364
39236c6e
A
1365 /* release the portset reference */
1366 ips_release(pset);
1c79356b 1367
0a7de745 1368 if (actual <= maxnames) {
1c79356b 1369 break;
0a7de745 1370 }
1c79356b
A
1371
1372 /* didn't have enough memory; allocate more */
1c79356b 1373 kmem_free(ipc_kernel_map, addr, size);
39236c6e
A
1374 size = vm_map_round_page(
1375 (actual * sizeof(mach_port_name_t)),
0a7de745
A
1376 VM_MAP_PAGE_MASK(ipc_kernel_map)) +
1377 VM_MAP_PAGE_SIZE(ipc_kernel_map);
1c79356b
A
1378 }
1379
1380 if (actual == 0) {
1381 memory = VM_MAP_COPY_NULL;
1382
1383 kmem_free(ipc_kernel_map, addr, size);
1384 } else {
1385 vm_size_t size_used;
1386 vm_size_t vm_size_used;
1387
1388 size_used = actual * sizeof(mach_port_name_t);
39236c6e
A
1389 vm_size_used = vm_map_round_page(
1390 size_used,
1391 VM_MAP_PAGE_MASK(ipc_kernel_map));
1c79356b
A
1392
1393 /*
1394 * Make used memory pageable and get it into
1395 * copied-in form. Free any unused memory.
1396 */
1397
39236c6e
A
1398 kr = vm_map_unwire(
1399 ipc_kernel_map,
1400 vm_map_trunc_page(addr,
0a7de745 1401 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 1402 vm_map_round_page(addr + vm_size_used,
0a7de745 1403 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 1404 FALSE);
1c79356b
A
1405 assert(kr == KERN_SUCCESS);
1406
91447636 1407 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr,
0a7de745 1408 (vm_map_size_t)size_used, TRUE, &memory);
1c79356b
A
1409 assert(kr == KERN_SUCCESS);
1410
0a7de745 1411 if (vm_size_used != size) {
1c79356b 1412 kmem_free(ipc_kernel_map,
0a7de745
A
1413 addr + vm_size_used, size - vm_size_used);
1414 }
1c79356b
A
1415 }
1416
1417 *members = (mach_port_name_t *) memory;
1418 *membersCnt = actual;
1419 return KERN_SUCCESS;
1420}
1421
1422/*
1423 * Routine: mach_port_move_member [kernel call]
1424 * Purpose:
1425 * If after is MACH_PORT_NULL, removes member
1426 * from the port set it is in. Otherwise, adds
1427 * member to after, removing it from any set
1428 * it might already be in.
1429 * Conditions:
1430 * Nothing locked.
1431 * Returns:
1432 * KERN_SUCCESS Moved the port.
1433 * KERN_INVALID_TASK The space is null.
1434 * KERN_INVALID_TASK The space is dead.
1435 * KERN_INVALID_NAME Member didn't denote a right.
1436 * KERN_INVALID_RIGHT Member didn't denote a receive right.
1437 * KERN_INVALID_NAME After didn't denote a right.
1438 * KERN_INVALID_RIGHT After didn't denote a port set right.
1439 * KERN_NOT_IN_SET
1440 * After is MACH_PORT_NULL and Member isn't in a port set.
1441 */
1442
1443kern_return_t
1444mach_port_move_member(
0a7de745
A
1445 ipc_space_t space,
1446 mach_port_name_t member,
1447 mach_port_name_t after)
1c79356b 1448{
cb323159 1449 ipc_object_t port_obj, ps_obj;
1c79356b 1450 ipc_port_t port;
cb323159 1451 ipc_pset_t nset = IPS_NULL;
1c79356b 1452 kern_return_t kr;
3e170ce0
A
1453 uint64_t wq_link_id = 0;
1454 uint64_t wq_reserved_prepost = 0;
1c79356b 1455
0a7de745 1456 if (space == IS_NULL) {
1c79356b 1457 return KERN_INVALID_TASK;
0a7de745 1458 }
1c79356b 1459
0a7de745 1460 if (!MACH_PORT_VALID(member)) {
1c79356b 1461 return KERN_INVALID_RIGHT;
0a7de745 1462 }
1c79356b 1463
3e170ce0 1464 if (after == MACH_PORT_DEAD) {
1c79356b 1465 return KERN_INVALID_RIGHT;
3e170ce0
A
1466 } else if (after == MACH_PORT_NULL) {
1467 wq_link_id = 0;
1468 } else {
1469 /*
1470 * We reserve both a link, and
1471 * enough prepost objects to complete
1472 * the set move atomically - we can't block
1473 * while we're holding the space lock, and
1474 * the ipc_pset_add calls ipc_mqueue_add
1475 * which may have to prepost this port onto
1476 * this set.
1477 */
1478 wq_link_id = waitq_link_reserve(NULL);
1479 wq_reserved_prepost = waitq_prepost_reserve(NULL, 10,
0a7de745 1480 WAITQ_DONT_LOCK);
d9a64523 1481 kr = ipc_pset_lazy_allocate(space, after);
0a7de745 1482 if (kr != KERN_SUCCESS) {
d9a64523 1483 goto done;
0a7de745 1484 }
3e170ce0 1485 }
1c79356b 1486
cb323159
A
1487 if (after != MACH_PORT_NULL) {
1488 kr = ipc_object_translate_two(space,
1489 member, MACH_PORT_RIGHT_RECEIVE, &port_obj,
1490 after, MACH_PORT_RIGHT_PORT_SET, &ps_obj);
1491 } else {
1492 kr = ipc_object_translate(space,
1493 member, MACH_PORT_RIGHT_RECEIVE, &port_obj);
1494 }
0a7de745 1495 if (kr != KERN_SUCCESS) {
316670eb 1496 goto done;
0a7de745 1497 }
1c79356b 1498
cb323159
A
1499 port = ip_object_to_port(port_obj);
1500 if (after != MACH_PORT_NULL) {
1501 nset = ips_object_to_pset(ps_obj);
1c79356b 1502 }
cb323159 1503 /* port and nset are locked */
1c79356b 1504
3e170ce0 1505 ipc_pset_remove_from_all(port);
1c79356b 1506
cb323159 1507 if (after != MACH_PORT_NULL) {
3e170ce0 1508 kr = ipc_pset_add(nset, port, &wq_link_id, &wq_reserved_prepost);
1c79356b
A
1509 ips_unlock(nset);
1510 }
cb323159 1511
1c79356b 1512 ip_unlock(port);
316670eb 1513
0a7de745 1514done:
3e170ce0
A
1515 /*
1516 * on success the ipc_pset_add() will consume the wq_link_id
1517 * value (resetting it to 0), so this function is always safe to call.
1518 */
1519 waitq_link_release(wq_link_id);
1520 waitq_prepost_release_reserve(wq_reserved_prepost);
316670eb 1521
1c79356b
A
1522 return kr;
1523}
1524
1525/*
1526 * Routine: mach_port_request_notification [kernel call]
1527 * Purpose:
1528 * Requests a notification. The caller supplies
1529 * a send-once right for the notification to use,
1530 * and the call returns the previously registered
1531 * send-once right, if any. Possible types:
1532 *
1533 * MACH_NOTIFY_PORT_DESTROYED
1534 * Requests a port-destroyed notification
1535 * for a receive right. Sync should be zero.
1536 * MACH_NOTIFY_NO_SENDERS
1537 * Requests a no-senders notification for a
1538 * receive right. If there are currently no
1539 * senders, sync is less than or equal to the
1540 * current make-send count, and a send-once right
1541 * is supplied, then an immediate no-senders
1542 * notification is generated.
1543 * MACH_NOTIFY_DEAD_NAME
1544 * Requests a dead-name notification for a send
1545 * or receive right. If the name is already a
1546 * dead name, sync is non-zero, and a send-once
1547 * right is supplied, then an immediate dead-name
1548 * notification is generated.
1549 * Conditions:
1550 * Nothing locked.
1551 * Returns:
1552 * KERN_SUCCESS Requested a notification.
1553 * KERN_INVALID_TASK The space is null.
1554 * KERN_INVALID_TASK The space is dead.
1555 * KERN_INVALID_VALUE Bad id value.
1556 * KERN_INVALID_NAME Name doesn't denote a right.
1557 * KERN_INVALID_RIGHT Name doesn't denote appropriate right.
1558 * KERN_INVALID_CAPABILITY The notify port is dead.
1559 * MACH_NOTIFY_PORT_DESTROYED:
1560 * KERN_INVALID_VALUE Sync isn't zero.
cb323159 1561 * KERN_FAILURE Re-registering for this notification
1c79356b
A
1562 * MACH_NOTIFY_DEAD_NAME:
1563 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1564 * KERN_INVALID_ARGUMENT Name denotes dead name, but
1565 * sync is zero or notify is IP_NULL.
1566 * KERN_UREFS_OVERFLOW Name denotes dead name, but
1567 * generating immediate notif. would overflow urefs.
1568 */
1569
1570kern_return_t
1571mach_port_request_notification(
0a7de745
A
1572 ipc_space_t space,
1573 mach_port_name_t name,
1574 mach_msg_id_t id,
1575 mach_port_mscount_t sync,
1576 ipc_port_t notify,
1577 ipc_port_t *previousp)
1c79356b
A
1578{
1579 kern_return_t kr;
1c79356b 1580
0a7de745 1581 if (space == IS_NULL) {
1c79356b 1582 return KERN_INVALID_TASK;
0a7de745 1583 }
1c79356b 1584
0a7de745 1585 if (notify == IP_DEAD) {
1c79356b 1586 return KERN_INVALID_CAPABILITY;
0a7de745 1587 }
1c79356b 1588
0a7de745 1589#if NOTYET
1c79356b
A
1590 /*
1591 * Requesting notifications on RPC ports is an error.
1592 */
91447636
A
1593 {
1594 ipc_port_t port;
0a7de745 1595 ipc_entry_t entry;
1c79356b 1596
0a7de745
A
1597 kr = ipc_right_lookup_write(space, name, &entry);
1598 if (kr != KERN_SUCCESS) {
91447636 1599 return kr;
0a7de745 1600 }
1c79356b 1601
cb323159 1602 port = ip_object_to_port(entry->ie_object);
91447636
A
1603
1604 if (port->ip_subsystem != NULL) {
1605 is_write_unlock(space);
0a7de745 1606 panic("mach_port_request_notification: on RPC port!!");
91447636
A
1607 return KERN_INVALID_CAPABILITY;
1608 }
1c79356b 1609 is_write_unlock(space);
1c79356b 1610 }
0a7de745 1611#endif /* NOTYET */
1c79356b
A
1612
1613
1614 switch (id) {
0a7de745 1615 case MACH_NOTIFY_PORT_DESTROYED: {
cb323159 1616 ipc_port_t port;
1c79356b 1617
0a7de745 1618 if (sync != 0) {
1c79356b 1619 return KERN_INVALID_VALUE;
0a7de745 1620 }
1c79356b 1621
0a7de745 1622 if (!MACH_PORT_VALID(name)) {
1c79356b 1623 return KERN_INVALID_RIGHT;
0a7de745 1624 }
1c79356b
A
1625
1626 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1627 if (kr != KERN_SUCCESS) {
1c79356b 1628 return kr;
0a7de745 1629 }
1c79356b
A
1630 /* port is locked and active */
1631
00867663
A
1632 /* you cannot register for port death notifications on a kobject */
1633 if (ip_kotype(port) != IKOT_NONE) {
1634 ip_unlock(port);
1635 return KERN_INVALID_RIGHT;
1636 }
1637
cb323159
A
1638 /* Allow only one registeration of this notification */
1639 if (port->ip_pdrequest != IP_NULL) {
1640 ip_unlock(port);
1641 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_KERN_FAILURE);
1642 return KERN_FAILURE;
1643 }
1c79356b 1644
cb323159
A
1645 ipc_port_pdrequest(port, notify, previousp);
1646 /* port is unlocked */
1647 assert(*previousp == IP_NULL);
1c79356b 1648 break;
0a7de745 1649 }
1c79356b 1650
0a7de745 1651 case MACH_NOTIFY_NO_SENDERS: {
1c79356b
A
1652 ipc_port_t port;
1653
0a7de745 1654 if (!MACH_PORT_VALID(name)) {
1c79356b 1655 return KERN_INVALID_RIGHT;
0a7de745 1656 }
1c79356b
A
1657
1658 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1659 if (kr != KERN_SUCCESS) {
1c79356b 1660 return kr;
0a7de745 1661 }
1c79356b
A
1662 /* port is locked and active */
1663
1664 ipc_port_nsrequest(port, sync, notify, previousp);
1665 /* port is unlocked */
1666 break;
0a7de745 1667 }
1c79356b 1668
0a7de745 1669 case MACH_NOTIFY_SEND_POSSIBLE:
6d2010ae 1670
0a7de745
A
1671 if (!MACH_PORT_VALID(name)) {
1672 return KERN_INVALID_ARGUMENT;
6d2010ae
A
1673 }
1674
1675 kr = ipc_right_request_alloc(space, name, sync != 0,
0a7de745
A
1676 TRUE, notify, previousp);
1677 if (kr != KERN_SUCCESS) {
6d2010ae 1678 return kr;
0a7de745 1679 }
6d2010ae
A
1680 break;
1681
0a7de745 1682 case MACH_NOTIFY_DEAD_NAME:
1c79356b 1683
0a7de745 1684 if (!MACH_PORT_VALID(name)) {
1c79356b
A
1685 /*
1686 * Already dead.
1687 * Should do immediate delivery check -
1688 * will do that in the near future.
1689 */
0a7de745 1690 return KERN_INVALID_ARGUMENT;
1c79356b
A
1691 }
1692
6d2010ae 1693 kr = ipc_right_request_alloc(space, name, sync != 0,
0a7de745
A
1694 FALSE, notify, previousp);
1695 if (kr != KERN_SUCCESS) {
1c79356b 1696 return kr;
0a7de745 1697 }
1c79356b
A
1698 break;
1699
0a7de745 1700 default:
1c79356b
A
1701 return KERN_INVALID_VALUE;
1702 }
1703
1704 return KERN_SUCCESS;
1705}
1706
1707/*
1708 * Routine: mach_port_insert_right [kernel call]
1709 * Purpose:
1710 * Inserts a right into a space, as if the space
1711 * voluntarily received the right in a message,
1712 * except that the right gets the specified name.
1713 * Conditions:
1714 * Nothing locked.
1715 * Returns:
1716 * KERN_SUCCESS Inserted the right.
1717 * KERN_INVALID_TASK The space is null.
1718 * KERN_INVALID_TASK The space is dead.
1719 * KERN_INVALID_VALUE The name isn't a legal name.
1720 * KERN_NAME_EXISTS The name already denotes a right.
1721 * KERN_INVALID_VALUE Message doesn't carry a port right.
1722 * KERN_INVALID_CAPABILITY Port is null or dead.
1723 * KERN_UREFS_OVERFLOW Urefs limit would be exceeded.
1724 * KERN_RIGHT_EXISTS Space has rights under another name.
1725 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1726 */
1727
1728kern_return_t
1729mach_port_insert_right(
0a7de745
A
1730 ipc_space_t space,
1731 mach_port_name_t name,
1732 ipc_port_t poly,
1733 mach_msg_type_name_t polyPoly)
1c79356b 1734{
0a7de745 1735 if (space == IS_NULL) {
1c79356b 1736 return KERN_INVALID_TASK;
0a7de745 1737 }
1c79356b
A
1738
1739 if (!MACH_PORT_VALID(name) ||
0a7de745 1740 !MACH_MSG_TYPE_PORT_ANY_RIGHT(polyPoly)) {
1c79356b 1741 return KERN_INVALID_VALUE;
0a7de745 1742 }
1c79356b 1743
cb323159 1744 if (!IP_VALID(poly)) {
1c79356b 1745 return KERN_INVALID_CAPABILITY;
0a7de745 1746 }
1c79356b 1747
cb323159
A
1748 return ipc_object_copyout_name(space, ip_to_object(poly),
1749 polyPoly, name);
1c79356b
A
1750}
1751
1752/*
1753 * Routine: mach_port_extract_right [kernel call]
1754 * Purpose:
1755 * Extracts a right from a space, as if the space
1756 * voluntarily sent the right to the caller.
1757 * Conditions:
1758 * Nothing locked.
1759 * Returns:
1760 * KERN_SUCCESS Extracted the right.
1761 * KERN_INVALID_TASK The space is null.
1762 * KERN_INVALID_TASK The space is dead.
1763 * KERN_INVALID_VALUE Requested type isn't a port right.
1764 * KERN_INVALID_NAME Name doesn't denote a right.
1765 * KERN_INVALID_RIGHT Name doesn't denote appropriate right.
1766 */
1767
1768kern_return_t
1769mach_port_extract_right(
0a7de745
A
1770 ipc_space_t space,
1771 mach_port_name_t name,
1772 mach_msg_type_name_t msgt_name,
1773 ipc_port_t *poly,
1774 mach_msg_type_name_t *polyPoly)
1c79356b
A
1775{
1776 kern_return_t kr;
1777
0a7de745 1778 if (space == IS_NULL) {
1c79356b 1779 return KERN_INVALID_TASK;
0a7de745 1780 }
1c79356b 1781
0a7de745 1782 if (!MACH_MSG_TYPE_PORT_ANY(msgt_name)) {
1c79356b 1783 return KERN_INVALID_VALUE;
0a7de745 1784 }
1c79356b
A
1785
1786 if (!MACH_PORT_VALID(name)) {
1787 /*
1788 * really should copy out a dead name, if it is a send or
1789 * send-once right being copied, but instead return an
1790 * error for now.
1791 */
1792 return KERN_INVALID_RIGHT;
1793 }
1794
cb323159
A
1795 kr = ipc_object_copyin(space, name, msgt_name, (ipc_object_t *) poly, 0, NULL,
1796 IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND);
1c79356b 1797
0a7de745 1798 if (kr == KERN_SUCCESS) {
1c79356b 1799 *polyPoly = ipc_object_copyin_type(msgt_name);
0a7de745 1800 }
1c79356b
A
1801 return kr;
1802}
1803
39236c6e
A
1804/*
1805 * Routine: mach_port_get_status_helper [helper]
1806 * Purpose:
1807 * Populates a mach_port_status_t structure with
1808 * port information.
1809 * Conditions:
1810 * Port needs to be locked
1811 * Returns:
1812 * None.
1813 */
cb323159 1814static void
0a7de745
A
1815mach_port_get_status_helper(
1816 ipc_port_t port,
1817 mach_port_status_t *statusp)
39236c6e 1818{
39236c6e 1819 imq_lock(&port->ip_messages);
3e170ce0
A
1820 /* don't leak set IDs, just indicate that the port is in one or not */
1821 statusp->mps_pset = !!(port->ip_in_pset);
39236c6e
A
1822 statusp->mps_seqno = port->ip_messages.imq_seqno;
1823 statusp->mps_qlimit = port->ip_messages.imq_qlimit;
1824 statusp->mps_msgcount = port->ip_messages.imq_msgcount;
1825 imq_unlock(&port->ip_messages);
743345f9 1826
39236c6e
A
1827 statusp->mps_mscount = port->ip_mscount;
1828 statusp->mps_sorights = port->ip_sorights;
1829 statusp->mps_srights = port->ip_srights > 0;
1830 statusp->mps_pdrequest = port->ip_pdrequest != IP_NULL;
1831 statusp->mps_nsrequest = port->ip_nsrequest != IP_NULL;
1832 statusp->mps_flags = 0;
fe8ab488
A
1833 if (port->ip_impdonation) {
1834 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_IMP_DONATION;
1835 if (port->ip_tempowner) {
1836 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_TEMPOWNER;
1837 if (IIT_NULL != port->ip_imp_task) {
1838 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_TASKPTR;
1839 }
1840 }
1841 }
1842 if (port->ip_guarded) {
1843 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_GUARDED;
1844 if (port->ip_strict_guard) {
1845 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_STRICT_GUARD;
1846 }
cb323159
A
1847 if (port->ip_immovable_receive) {
1848 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_GUARD_IMMOVABLE_RECEIVE;
1849 }
1850 }
1851 if (port->ip_no_grant) {
1852 statusp->mps_flags |= MACH_PORT_STATUS_FLAG_NO_GRANT;
fe8ab488 1853 }
39236c6e
A
1854 return;
1855}
1856
1857
1c79356b
A
1858
1859kern_return_t
1860mach_port_get_attributes(
0a7de745
A
1861 ipc_space_t space,
1862 mach_port_name_t name,
1863 int flavor,
1864 mach_port_info_t info,
1865 mach_msg_type_number_t *count)
1c79356b
A
1866{
1867 ipc_port_t port;
1868 kern_return_t kr;
1869
0a7de745 1870 if (space == IS_NULL) {
1c79356b 1871 return KERN_INVALID_TASK;
0a7de745 1872 }
1c79356b 1873
0a7de745
A
1874 switch (flavor) {
1875 case MACH_PORT_LIMITS_INFO: {
1876 mach_port_limits_t *lp = (mach_port_limits_t *)info;
1c79356b 1877
0a7de745
A
1878 if (*count < MACH_PORT_LIMITS_INFO_COUNT) {
1879 return KERN_FAILURE;
1880 }
1c79356b 1881
0a7de745 1882 if (!MACH_PORT_VALID(name)) {
1c79356b
A
1883 *count = 0;
1884 break;
1885 }
1c79356b 1886
0a7de745
A
1887 kr = ipc_port_translate_receive(space, name, &port);
1888 if (kr != KERN_SUCCESS) {
1889 return kr;
1890 }
1891 /* port is locked and active */
1892
1893 lp->mpl_qlimit = port->ip_messages.imq_qlimit;
1894 *count = MACH_PORT_LIMITS_INFO_COUNT;
1895 ip_unlock(port);
1896 break;
1897 }
1c79356b 1898
0a7de745 1899 case MACH_PORT_RECEIVE_STATUS: {
39236c6e 1900 mach_port_status_t *statusp = (mach_port_status_t *)info;
0a7de745
A
1901
1902 if (*count < MACH_PORT_RECEIVE_STATUS_COUNT) {
39236c6e 1903 return KERN_FAILURE;
0a7de745 1904 }
39236c6e 1905
0a7de745 1906 if (!MACH_PORT_VALID(name)) {
1c79356b 1907 return KERN_INVALID_RIGHT;
0a7de745 1908 }
1c79356b 1909
39236c6e 1910 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1911 if (kr != KERN_SUCCESS) {
39236c6e 1912 return kr;
0a7de745 1913 }
39236c6e
A
1914 /* port is locked and active */
1915 mach_port_get_status_helper(port, statusp);
1916 *count = MACH_PORT_RECEIVE_STATUS_COUNT;
1917 ip_unlock(port);
1918 break;
1919 }
0a7de745 1920
1c79356b 1921 case MACH_PORT_DNREQUESTS_SIZE: {
0a7de745
A
1922 ipc_port_request_t table;
1923
1924 if (*count < MACH_PORT_DNREQUESTS_SIZE_COUNT) {
1925 return KERN_FAILURE;
1926 }
1c79356b
A
1927
1928 if (!MACH_PORT_VALID(name)) {
1929 *(int *)info = 0;
1930 break;
1931 }
1932
0a7de745
A
1933 kr = ipc_port_translate_receive(space, name, &port);
1934 if (kr != KERN_SUCCESS) {
1935 return kr;
1936 }
1937 /* port is locked and active */
1938
6d2010ae 1939 table = port->ip_requests;
0a7de745 1940 if (table == IPR_NULL) {
1c79356b 1941 *(int *)info = 0;
0a7de745 1942 } else {
1c79356b 1943 *(int *)info = table->ipr_size->its_size;
0a7de745
A
1944 }
1945 *count = MACH_PORT_DNREQUESTS_SIZE_COUNT;
1946 ip_unlock(port);
1c79356b
A
1947 break;
1948 }
1949
39236c6e
A
1950 case MACH_PORT_INFO_EXT: {
1951 mach_port_info_ext_t *mp_info = (mach_port_info_ext_t *)info;
0a7de745 1952 if (*count < MACH_PORT_INFO_EXT_COUNT) {
39236c6e 1953 return KERN_FAILURE;
0a7de745
A
1954 }
1955
1956 if (!MACH_PORT_VALID(name)) {
39236c6e 1957 return KERN_INVALID_RIGHT;
0a7de745
A
1958 }
1959
39236c6e 1960 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1961 if (kr != KERN_SUCCESS) {
39236c6e 1962 return kr;
0a7de745 1963 }
39236c6e
A
1964 /* port is locked and active */
1965 mach_port_get_status_helper(port, &mp_info->mpie_status);
1966 mp_info->mpie_boost_cnt = port->ip_impcount;
1967 *count = MACH_PORT_INFO_EXT_COUNT;
1968 ip_unlock(port);
1969 break;
1970 }
1971
0a7de745 1972 default:
1c79356b 1973 return KERN_INVALID_ARGUMENT;
0a7de745
A
1974 /*NOTREACHED*/
1975 }
1c79356b
A
1976
1977 return KERN_SUCCESS;
1978}
1979
1980kern_return_t
1981mach_port_set_attributes(
0a7de745
A
1982 ipc_space_t space,
1983 mach_port_name_t name,
1984 int flavor,
1985 mach_port_info_t info,
1986 mach_msg_type_number_t count)
1c79356b
A
1987{
1988 ipc_port_t port;
1989 kern_return_t kr;
0a7de745
A
1990
1991 if (space == IS_NULL) {
1c79356b 1992 return KERN_INVALID_TASK;
0a7de745
A
1993 }
1994
1995 switch (flavor) {
1996 case MACH_PORT_LIMITS_INFO: {
1997 mach_port_limits_t *mplp = (mach_port_limits_t *)info;
1998
1999 if (count < MACH_PORT_LIMITS_INFO_COUNT) {
2000 return KERN_FAILURE;
2001 }
2002
2003 if (mplp->mpl_qlimit > MACH_PORT_QLIMIT_MAX) {
2004 return KERN_INVALID_VALUE;
2005 }
1c79356b 2006
0a7de745 2007 if (!MACH_PORT_VALID(name)) {
1c79356b 2008 return KERN_INVALID_RIGHT;
0a7de745 2009 }
1c79356b 2010
0a7de745
A
2011 kr = ipc_port_translate_receive(space, name, &port);
2012 if (kr != KERN_SUCCESS) {
2013 return kr;
2014 }
2015 /* port is locked and active */
1c79356b 2016
0a7de745
A
2017 ipc_mqueue_set_qlimit(&port->ip_messages, mplp->mpl_qlimit);
2018 ip_unlock(port);
2019 break;
2020 }
1c79356b 2021 case MACH_PORT_DNREQUESTS_SIZE: {
0a7de745
A
2022 if (count < MACH_PORT_DNREQUESTS_SIZE_COUNT) {
2023 return KERN_FAILURE;
2024 }
1c79356b 2025
0a7de745 2026 if (!MACH_PORT_VALID(name)) {
1c79356b 2027 return KERN_INVALID_RIGHT;
0a7de745
A
2028 }
2029
2030 kr = ipc_port_translate_receive(space, name, &port);
2031 if (kr != KERN_SUCCESS) {
2032 return kr;
2033 }
2034 /* port is locked and active */
2035
6d2010ae 2036 kr = ipc_port_request_grow(port, *(int *)info);
0a7de745 2037 if (kr != KERN_SUCCESS) {
1c79356b 2038 return kr;
0a7de745 2039 }
1c79356b
A
2040 break;
2041 }
39236c6e 2042 case MACH_PORT_TEMPOWNER:
0a7de745 2043 if (!MACH_PORT_VALID(name)) {
39236c6e 2044 return KERN_INVALID_RIGHT;
0a7de745 2045 }
39236c6e 2046
fe8ab488 2047 ipc_importance_task_t release_imp_task = IIT_NULL;
39236c6e
A
2048 natural_t assertcnt = 0;
2049
2050 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 2051 if (kr != KERN_SUCCESS) {
39236c6e 2052 return kr;
0a7de745 2053 }
39236c6e
A
2054 /* port is locked and active */
2055
0a7de745 2056 /*
fe8ab488 2057 * don't allow temp-owner importance donation if user
5ba3f43e
A
2058 * associated it with a kobject already (timer, host_notify target),
2059 * or is a special reply port.
fe8ab488 2060 */
cb323159 2061 if (ip_is_kobject(port) || port->ip_specialreply) {
fe8ab488
A
2062 ip_unlock(port);
2063 return KERN_INVALID_ARGUMENT;
2064 }
2065
39236c6e 2066 if (port->ip_tempowner != 0) {
fe8ab488 2067 if (IIT_NULL != port->ip_imp_task) {
39236c6e 2068 release_imp_task = port->ip_imp_task;
fe8ab488 2069 port->ip_imp_task = IIT_NULL;
39236c6e
A
2070 assertcnt = port->ip_impcount;
2071 }
2072 } else {
2073 assertcnt = port->ip_impcount;
2074 }
2075
2076 port->ip_impdonation = 1;
2077 port->ip_tempowner = 1;
2078 ip_unlock(port);
2079
2080#if IMPORTANCE_INHERITANCE
2081 /* drop assertions from previous destination task */
fe8ab488
A
2082 if (release_imp_task != IIT_NULL) {
2083 assert(ipc_importance_task_is_any_receiver_type(release_imp_task));
0a7de745 2084 if (assertcnt > 0) {
fe8ab488 2085 ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
0a7de745 2086 }
fe8ab488 2087 ipc_importance_task_release(release_imp_task);
39236c6e 2088 } else if (assertcnt > 0) {
fe8ab488
A
2089 release_imp_task = current_task()->task_imp_base;
2090 if (release_imp_task != IIT_NULL &&
2091 ipc_importance_task_is_any_receiver_type(release_imp_task)) {
2092 ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
2093 }
39236c6e
A
2094 }
2095#else
0a7de745 2096 if (release_imp_task != IIT_NULL) {
fe8ab488 2097 ipc_importance_task_release(release_imp_task);
0a7de745 2098 }
39236c6e
A
2099#endif /* IMPORTANCE_INHERITANCE */
2100
2101 break;
fe8ab488 2102
39236c6e 2103#if IMPORTANCE_INHERITANCE
fe8ab488 2104 case MACH_PORT_DENAP_RECEIVER:
39236c6e 2105 case MACH_PORT_IMPORTANCE_RECEIVER:
0a7de745 2106 if (!MACH_PORT_VALID(name)) {
39236c6e 2107 return KERN_INVALID_RIGHT;
0a7de745 2108 }
39236c6e
A
2109
2110 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 2111 if (kr != KERN_SUCCESS) {
39236c6e 2112 return kr;
0a7de745 2113 }
39236c6e 2114
0a7de745 2115 /*
fe8ab488 2116 * don't allow importance donation if user associated
5ba3f43e
A
2117 * it with a kobject already (timer, host_notify target),
2118 * or is a special reply port.
fe8ab488 2119 */
cb323159 2120 if (ip_is_kobject(port) || port->ip_specialreply) {
fe8ab488
A
2121 ip_unlock(port);
2122 return KERN_INVALID_ARGUMENT;
2123 }
2124
2125 /* port is locked and active */
39236c6e
A
2126 port->ip_impdonation = 1;
2127 ip_unlock(port);
2128
2129 break;
2130#endif /* IMPORTANCE_INHERITANCE */
2131
0a7de745 2132 default:
1c79356b 2133 return KERN_INVALID_ARGUMENT;
0a7de745
A
2134 /*NOTREACHED*/
2135 }
1c79356b
A
2136 return KERN_SUCCESS;
2137}
2138
2139/*
2140 * Routine: mach_port_insert_member [kernel call]
2141 * Purpose:
2142 * Add the receive right, specified by name, to
2143 * a portset.
2144 * The port cannot already be a member of the set.
2145 * Conditions:
2146 * Nothing locked.
2147 * Returns:
2148 * KERN_SUCCESS Moved the port.
2149 * KERN_INVALID_TASK The space is null.
2150 * KERN_INVALID_TASK The space is dead.
2151 * KERN_INVALID_NAME name didn't denote a right.
2152 * KERN_INVALID_RIGHT name didn't denote a receive right.
2153 * KERN_INVALID_NAME pset_name didn't denote a right.
2154 * KERN_INVALID_RIGHT pset_name didn't denote a portset right.
2155 * KERN_ALREADY_IN_SET name was already a member of pset.
2156 */
2157
2158kern_return_t
2159mach_port_insert_member(
0a7de745
A
2160 ipc_space_t space,
2161 mach_port_name_t name,
2162 mach_port_name_t psname)
1c79356b
A
2163{
2164 ipc_object_t obj;
2165 ipc_object_t psobj;
2166 kern_return_t kr;
3e170ce0
A
2167 uint64_t wq_link_id;
2168 uint64_t wq_reserved_prepost;
1c79356b 2169
0a7de745 2170 if (space == IS_NULL) {
1c79356b 2171 return KERN_INVALID_TASK;
0a7de745 2172 }
1c79356b 2173
0a7de745 2174 if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) {
1c79356b 2175 return KERN_INVALID_RIGHT;
0a7de745 2176 }
1c79356b 2177
3e170ce0
A
2178 wq_link_id = waitq_link_reserve(NULL);
2179 wq_reserved_prepost = waitq_prepost_reserve(NULL, 10,
0a7de745 2180 WAITQ_DONT_LOCK);
d9a64523 2181 kr = ipc_pset_lazy_allocate(space, psname);
0a7de745 2182 if (kr != KERN_SUCCESS) {
d9a64523 2183 goto done;
0a7de745 2184 }
d9a64523 2185
316670eb 2186
0a7de745
A
2187 kr = ipc_object_translate_two(space,
2188 name, MACH_PORT_RIGHT_RECEIVE, &obj,
2189 psname, MACH_PORT_RIGHT_PORT_SET, &psobj);
2190 if (kr != KERN_SUCCESS) {
316670eb 2191 goto done;
0a7de745 2192 }
1c79356b
A
2193
2194 /* obj and psobj are locked (and were locked in that order) */
2195 assert(psobj != IO_NULL);
2196 assert(obj != IO_NULL);
2197
cb323159
A
2198 kr = ipc_pset_add(ips_object_to_pset(psobj), ip_object_to_port(obj),
2199 &wq_link_id, &wq_reserved_prepost);
3e170ce0 2200
1c79356b
A
2201 io_unlock(psobj);
2202 io_unlock(obj);
316670eb 2203
0a7de745 2204done:
3e170ce0
A
2205 /* on success, wq_link_id is reset to 0, so this is always safe */
2206 waitq_link_release(wq_link_id);
2207 waitq_prepost_release_reserve(wq_reserved_prepost);
316670eb 2208
1c79356b
A
2209 return kr;
2210}
2211
2212/*
2213 * Routine: mach_port_extract_member [kernel call]
2214 * Purpose:
2215 * Remove a port from one portset that it is a member of.
2216 * Conditions:
2217 * Nothing locked.
2218 * Returns:
2219 * KERN_SUCCESS Moved the port.
2220 * KERN_INVALID_TASK The space is null.
2221 * KERN_INVALID_TASK The space is dead.
2222 * KERN_INVALID_NAME Member didn't denote a right.
2223 * KERN_INVALID_RIGHT Member didn't denote a receive right.
2224 * KERN_INVALID_NAME After didn't denote a right.
2225 * KERN_INVALID_RIGHT After didn't denote a port set right.
2226 * KERN_NOT_IN_SET
2227 * After is MACH_PORT_NULL and Member isn't in a port set.
2228 */
2229
2230kern_return_t
2231mach_port_extract_member(
0a7de745
A
2232 ipc_space_t space,
2233 mach_port_name_t name,
2234 mach_port_name_t psname)
1c79356b 2235{
1c79356b
A
2236 ipc_object_t psobj;
2237 ipc_object_t obj;
2238 kern_return_t kr;
2239
0a7de745 2240 if (space == IS_NULL) {
1c79356b 2241 return KERN_INVALID_TASK;
0a7de745 2242 }
1c79356b 2243
0a7de745 2244 if (!MACH_PORT_VALID(name) || !MACH_PORT_VALID(psname)) {
1c79356b 2245 return KERN_INVALID_RIGHT;
0a7de745 2246 }
1c79356b 2247
0a7de745
A
2248 kr = ipc_object_translate_two(space,
2249 name, MACH_PORT_RIGHT_RECEIVE, &obj,
2250 psname, MACH_PORT_RIGHT_PORT_SET, &psobj);
2251 if (kr != KERN_SUCCESS) {
1c79356b 2252 return kr;
0a7de745 2253 }
1c79356b
A
2254
2255 /* obj and psobj are both locked (and were locked in that order) */
2256 assert(psobj != IO_NULL);
2257 assert(obj != IO_NULL);
2258
cb323159 2259 kr = ipc_pset_remove(ips_object_to_pset(psobj), ip_object_to_port(obj));
3e170ce0 2260
1c79356b
A
2261 io_unlock(psobj);
2262 io_unlock(obj);
316670eb 2263
1c79356b
A
2264 return kr;
2265}
2266
91447636
A
2267/*
2268 * task_set_port_space:
2269 *
2270 * Set port name space of task to specified size.
2271 */
2272kern_return_t
2273task_set_port_space(
0a7de745
A
2274 ipc_space_t space,
2275 int table_entries)
91447636
A
2276{
2277 kern_return_t kr;
0a7de745
A
2278
2279 if (space == IS_NULL) {
490019cf 2280 return KERN_INVALID_TASK;
0a7de745 2281 }
490019cf 2282
91447636 2283 is_write_lock(space);
6d2010ae 2284
316670eb 2285 if (!is_active(space)) {
6d2010ae
A
2286 is_write_unlock(space);
2287 return KERN_INVALID_TASK;
2288 }
2289
91447636 2290 kr = ipc_entry_grow_table(space, table_entries);
0a7de745 2291 if (kr == KERN_SUCCESS) {
91447636 2292 is_write_unlock(space);
0a7de745 2293 }
91447636
A
2294 return kr;
2295}
2296
39236c6e
A
2297/*
2298 * Routine: mach_port_guard_locked [helper routine]
2299 * Purpose:
2300 * Sets a new guard for a locked port.
2301 * Conditions:
2302 * Port Locked.
2303 * Returns:
2304 * KERN_SUCCESS Port Guarded.
2305 * KERN_INVALID_ARGUMENT Port already contains a context/guard.
2306 */
2307static kern_return_t
2308mach_port_guard_locked(
0a7de745
A
2309 ipc_port_t port,
2310 uint64_t guard,
cb323159 2311 uint64_t flags)
39236c6e 2312{
0a7de745 2313 if (port->ip_context) {
39236c6e 2314 return KERN_INVALID_ARGUMENT;
0a7de745 2315 }
39236c6e 2316
cb323159
A
2317 int strict = (flags & MPG_STRICT)? 1 : 0;
2318 int immovable_receive = (flags & MPG_IMMOVABLE_RECEIVE)? 1 : 0;
2319
2320 imq_lock(&port->ip_messages);
39236c6e
A
2321 port->ip_context = guard;
2322 port->ip_guarded = 1;
cb323159
A
2323 port->ip_strict_guard = strict;
2324 /* ip_immovable_receive bit is sticky and can't be un-guarded */
2325 if (!port->ip_immovable_receive) {
2326 port->ip_immovable_receive = immovable_receive;
2327 }
2328 imq_unlock(&port->ip_messages);
2329
39236c6e
A
2330 return KERN_SUCCESS;
2331}
2332
2333/*
2334 * Routine: mach_port_unguard_locked [helper routine]
2335 * Purpose:
2336 * Removes guard for a locked port.
2337 * Conditions:
2338 * Port Locked.
2339 * Returns:
2340 * KERN_SUCCESS Port Unguarded.
2341 * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
2342 * This also raises a EXC_GUARD exception.
2343 */
2344static kern_return_t
2345mach_port_unguard_locked(
0a7de745
A
2346 ipc_port_t port,
2347 mach_port_name_t name,
2348 uint64_t guard)
39236c6e
A
2349{
2350 /* Port locked and active */
2351 if (!port->ip_guarded) {
2352 /* Port already unguarded; Raise exception */
2353 mach_port_guard_exception(name, guard, 0, kGUARD_EXC_UNGUARDED);
2354 return KERN_INVALID_ARGUMENT;
2355 }
2356
2357 if (port->ip_context != guard) {
2358 /* Incorrect guard; Raise exception */
2359 mach_port_guard_exception(name, guard, port->ip_context, kGUARD_EXC_INCORRECT_GUARD);
2360 return KERN_INVALID_ARGUMENT;
2361 }
2362
cb323159 2363 imq_lock(&port->ip_messages);
39236c6e
A
2364 port->ip_context = 0;
2365 port->ip_guarded = port->ip_strict_guard = 0;
cb323159
A
2366 /* Don't clear the ip_immovable_receive bit */
2367 imq_unlock(&port->ip_messages);
2368
39236c6e
A
2369 return KERN_SUCCESS;
2370}
2371
2372
2373/*
2374 * Routine: mach_port_guard_exception [helper routine]
2375 * Purpose:
2376 * Marks the thread with AST_GUARD for mach port guard violation.
2377 * Also saves exception info in thread structure.
2378 * Conditions:
2379 * None.
2380 * Returns:
2381 * KERN_FAILURE Thread marked with AST_GUARD.
2382 */
d9a64523 2383void
39236c6e 2384mach_port_guard_exception(
0a7de745
A
2385 mach_port_name_t name,
2386 __unused uint64_t inguard,
2387 uint64_t portguard,
2388 unsigned reason)
39236c6e 2389{
5ba3f43e
A
2390 mach_exception_code_t code = 0;
2391 EXC_GUARD_ENCODE_TYPE(code, GUARD_TYPE_MACH_PORT);
2392 EXC_GUARD_ENCODE_FLAVOR(code, reason);
2393 EXC_GUARD_ENCODE_TARGET(code, name);
2394 mach_exception_subcode_t subcode = (uint64_t)portguard;
39236c6e 2395 thread_t t = current_thread();
cb323159
A
2396 boolean_t fatal = FALSE;
2397 if (t->task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) {
2398 fatal = TRUE;
2399 } else if (reason <= MAX_FATAL_kGUARD_EXC_CODE) {
2400 fatal = TRUE;
2401 }
2402 thread_guard_violation(t, code, subcode, fatal);
39236c6e
A
2403}
2404
2405
2406/*
2407 * Routine: mach_port_guard_ast
2408 * Purpose:
2409 * Raises an exception for mach port guard violation.
2410 * Conditions:
2411 * None.
2412 * Returns:
2413 * None.
2414 */
2415
2416void
d9a64523 2417mach_port_guard_ast(thread_t t,
0a7de745 2418 mach_exception_data_type_t code, mach_exception_data_type_t subcode)
39236c6e 2419{
d9a64523
A
2420 unsigned int reason = EXC_GUARD_DECODE_GUARD_FLAVOR(code);
2421 task_t task = t->task;
2422 unsigned int behavior = task->task_exc_guard;
2423 assert(task == current_task());
2424 assert(task != kernel_task);
5ba3f43e 2425
d9a64523 2426 switch (reason) {
0a7de745
A
2427 /*
2428 * Fatal Mach port guards - always delivered synchronously
2429 */
d9a64523
A
2430 case kGUARD_EXC_DESTROY:
2431 case kGUARD_EXC_MOD_REFS:
2432 case kGUARD_EXC_SET_CONTEXT:
2433 case kGUARD_EXC_UNGUARDED:
2434 case kGUARD_EXC_INCORRECT_GUARD:
cb323159
A
2435 case kGUARD_EXC_IMMOVABLE:
2436 case kGUARD_EXC_STRICT_REPLY:
d9a64523
A
2437 task_exception_notify(EXC_GUARD, code, subcode);
2438 task_bsdtask_kill(task);
2439 break;
2440
2441 default:
2442 /*
2443 * Mach port guards controlled by task settings.
2444 */
2445
2446 /* Is delivery enabled */
2447 if ((behavior & TASK_EXC_GUARD_MP_DELIVER) == 0) {
2448 return;
2449 }
2450
2451 /* If only once, make sure we're that once */
2452 while (behavior & TASK_EXC_GUARD_MP_ONCE) {
2453 uint32_t new_behavior = behavior & ~TASK_EXC_GUARD_MP_DELIVER;
2454
2455 if (OSCompareAndSwap(behavior, new_behavior, &task->task_exc_guard)) {
2456 break;
2457 }
2458 behavior = task->task_exc_guard;
2459 if ((behavior & TASK_EXC_GUARD_MP_DELIVER) == 0) {
2460 return;
2461 }
2462 }
2463
2464 /* Raise exception via corpse fork or synchronously */
2465 if ((task->task_exc_guard & TASK_EXC_GUARD_MP_CORPSE) &&
2466 (task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) == 0) {
2467 task_violated_guard(code, subcode, NULL);
2468 } else {
2469 task_exception_notify(EXC_GUARD, code, subcode);
2470 }
39236c6e 2471
d9a64523
A
2472 /* Terminate the task if desired */
2473 if (task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) {
2474 task_bsdtask_kill(task);
2475 }
2476 break;
2477 }
39236c6e
A
2478}
2479
2480/*
2481 * Routine: mach_port_construct [kernel call]
2482 * Purpose:
2483 * Constructs a mach port with the provided set of options.
2484 * Conditions:
2485 * None.
2486 * Returns:
2487 * KERN_SUCCESS The right is allocated.
2488 * KERN_INVALID_TASK The space is null.
2489 * KERN_INVALID_TASK The space is dead.
2490 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
2491 * KERN_NO_SPACE No room in space for another right.
2492 * KERN_FAILURE Illegal option values requested.
2493 */
2494
2495kern_return_t
2496mach_port_construct(
0a7de745
A
2497 ipc_space_t space,
2498 mach_port_options_t *options,
2499 uint64_t context,
2500 mach_port_name_t *name)
39236c6e 2501{
0a7de745
A
2502 kern_return_t kr;
2503 ipc_port_t port;
94ff46dc 2504 ipc_port_init_flags_t init_flags = IPC_PORT_INIT_MESSAGE_QUEUE;
39236c6e 2505
0a7de745
A
2506 if (space == IS_NULL) {
2507 return KERN_INVALID_TASK;
2508 }
39236c6e 2509
94ff46dc
A
2510 if (options->flags & MPO_INSERT_SEND_RIGHT) {
2511 init_flags |= IPC_PORT_INIT_MAKE_SEND_RIGHT;
2512 }
2513
39236c6e 2514 /* Allocate a new port in the IPC space */
94ff46dc 2515 kr = ipc_port_alloc(space, init_flags, name, &port);
0a7de745 2516 if (kr != KERN_SUCCESS) {
39236c6e 2517 return kr;
0a7de745
A
2518 }
2519
39236c6e
A
2520 /* Port locked and active */
2521 if (options->flags & MPO_CONTEXT_AS_GUARD) {
cb323159
A
2522 uint64_t flags = 0;
2523 if (options->flags & MPO_STRICT) {
2524 flags |= MPG_STRICT;
2525 }
2526 if (options->flags & MPO_IMMOVABLE_RECEIVE) {
2527 flags |= MPG_IMMOVABLE_RECEIVE;
2528 }
2529 kr = mach_port_guard_locked(port, (uint64_t) context, flags);
39236c6e
A
2530 /* A newly allocated and locked port should always be guarded successfully */
2531 assert(kr == KERN_SUCCESS);
2532 } else {
2533 port->ip_context = context;
2534 }
0a7de745 2535
39236c6e
A
2536 /* Unlock port */
2537 ip_unlock(port);
2538
2539 /* Set port attributes as requested */
2540
2541 if (options->flags & MPO_QLIMIT) {
2542 kr = mach_port_set_attributes(space, *name, MACH_PORT_LIMITS_INFO,
0a7de745
A
2543 (mach_port_info_t)&options->mpl, sizeof(options->mpl) / sizeof(int));
2544 if (kr != KERN_SUCCESS) {
2545 goto cleanup;
2546 }
39236c6e
A
2547 }
2548
2549 if (options->flags & MPO_TEMPOWNER) {
2550 kr = mach_port_set_attributes(space, *name, MACH_PORT_TEMPOWNER, NULL, 0);
0a7de745 2551 if (kr != KERN_SUCCESS) {
39236c6e 2552 goto cleanup;
0a7de745 2553 }
39236c6e
A
2554 }
2555
2556 if (options->flags & MPO_IMPORTANCE_RECEIVER) {
2557 kr = mach_port_set_attributes(space, *name, MACH_PORT_IMPORTANCE_RECEIVER, NULL, 0);
0a7de745 2558 if (kr != KERN_SUCCESS) {
39236c6e 2559 goto cleanup;
0a7de745 2560 }
39236c6e
A
2561 }
2562
fe8ab488
A
2563 if (options->flags & MPO_DENAP_RECEIVER) {
2564 kr = mach_port_set_attributes(space, *name, MACH_PORT_DENAP_RECEIVER, NULL, 0);
0a7de745 2565 if (kr != KERN_SUCCESS) {
fe8ab488 2566 goto cleanup;
0a7de745 2567 }
fe8ab488
A
2568 }
2569
39236c6e
A
2570 return KERN_SUCCESS;
2571
2572cleanup:
2573 /* Attempt to destroy port. If its already destroyed by some other thread, we're done */
cb323159
A
2574 (void) mach_port_destruct(space, *name,
2575 (options->flags & MPO_INSERT_SEND_RIGHT) ? -1 : 0, context);
39236c6e
A
2576 return kr;
2577}
2578
2579/*
2580 * Routine: mach_port_destruct [kernel call]
2581 * Purpose:
2582 * Destroys a mach port with appropriate guard
2583 * Conditions:
2584 * None.
2585 * Returns:
2586 * KERN_SUCCESS The name is destroyed.
2587 * KERN_INVALID_TASK The space is null.
2588 * KERN_INVALID_TASK The space is dead.
2589 * KERN_INVALID_NAME The name doesn't denote a right.
2590 * KERN_INVALID_RIGHT The right isn't correct.
2591 * KERN_INVALID_VALUE The delta for send right is incorrect.
2592 * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
2593 * This also raises a EXC_GUARD exception.
2594 */
2595
2596kern_return_t
2597mach_port_destruct(
0a7de745
A
2598 ipc_space_t space,
2599 mach_port_name_t name,
2600 mach_port_delta_t srdelta,
2601 uint64_t guard)
39236c6e 2602{
0a7de745
A
2603 kern_return_t kr;
2604 ipc_entry_t entry;
39236c6e 2605
0a7de745 2606 if (space == IS_NULL) {
39236c6e 2607 return KERN_INVALID_TASK;
0a7de745 2608 }
39236c6e 2609
0a7de745 2610 if (!MACH_PORT_VALID(name)) {
39236c6e 2611 return KERN_INVALID_NAME;
0a7de745 2612 }
39236c6e
A
2613
2614 /* Remove reference for receive right */
2615 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
2616 if (kr != KERN_SUCCESS) {
2617 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
39236c6e 2618 return kr;
d9a64523 2619 }
39236c6e 2620 /* space is write-locked and active */
0a7de745 2621 kr = ipc_right_destruct(space, name, entry, srdelta, guard); /* unlocks */
39236c6e
A
2622
2623 return kr;
2624}
2625
2626/*
2627 * Routine: mach_port_guard [kernel call]
2628 * Purpose:
2629 * Guard a mach port with specified guard value.
2630 * The context field of the port is used as the guard.
2631 * Conditions:
2632 * None.
2633 * Returns:
2634 * KERN_SUCCESS The name is destroyed.
2635 * KERN_INVALID_TASK The space is null.
2636 * KERN_INVALID_TASK The space is dead.
2637 * KERN_INVALID_NAME The name doesn't denote a right.
2638 * KERN_INVALID_RIGHT The right isn't correct.
2639 * KERN_INVALID_ARGUMENT Port already contains a context/guard.
2640 */
2641kern_return_t
2642mach_port_guard(
0a7de745
A
2643 ipc_space_t space,
2644 mach_port_name_t name,
2645 uint64_t guard,
2646 boolean_t strict)
39236c6e 2647{
0a7de745
A
2648 kern_return_t kr;
2649 ipc_port_t port;
cb323159 2650 uint64_t flags = 0;
39236c6e 2651
0a7de745 2652 if (space == IS_NULL) {
39236c6e 2653 return KERN_INVALID_TASK;
0a7de745 2654 }
39236c6e 2655
0a7de745 2656 if (!MACH_PORT_VALID(name)) {
39236c6e 2657 return KERN_INVALID_NAME;
0a7de745 2658 }
39236c6e
A
2659
2660 /* Guard can be applied only to receive rights */
2661 kr = ipc_port_translate_receive(space, name, &port);
d9a64523
A
2662 if (kr != KERN_SUCCESS) {
2663 mach_port_guard_exception(name, 0, 0,
0a7de745
A
2664 ((KERN_INVALID_NAME == kr) ?
2665 kGUARD_EXC_INVALID_NAME :
2666 kGUARD_EXC_INVALID_RIGHT));
39236c6e 2667 return kr;
d9a64523 2668 }
39236c6e
A
2669
2670 /* Port locked and active */
cb323159
A
2671 if (strict) {
2672 flags = MPG_STRICT;
2673 }
2674
2675 kr = mach_port_guard_locked(port, guard, flags);
39236c6e
A
2676 ip_unlock(port);
2677
d9a64523
A
2678 if (KERN_INVALID_ARGUMENT == kr) {
2679 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_ARGUMENT);
2680 }
39236c6e 2681
d9a64523 2682 return kr;
39236c6e
A
2683}
2684
2685/*
2686 * Routine: mach_port_unguard [kernel call]
2687 * Purpose:
2688 * Unguard a mach port with specified guard value.
2689 * Conditions:
2690 * None.
2691 * Returns:
2692 * KERN_SUCCESS The name is destroyed.
2693 * KERN_INVALID_TASK The space is null.
2694 * KERN_INVALID_TASK The space is dead.
2695 * KERN_INVALID_NAME The name doesn't denote a right.
2696 * KERN_INVALID_RIGHT The right isn't correct.
2697 * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
2698 * This also raises a EXC_GUARD exception.
2699 */
2700kern_return_t
2701mach_port_unguard(
0a7de745
A
2702 ipc_space_t space,
2703 mach_port_name_t name,
2704 uint64_t guard)
39236c6e 2705{
0a7de745
A
2706 kern_return_t kr;
2707 ipc_port_t port;
39236c6e 2708
0a7de745 2709 if (space == IS_NULL) {
39236c6e 2710 return KERN_INVALID_TASK;
0a7de745 2711 }
39236c6e 2712
0a7de745 2713 if (!MACH_PORT_VALID(name)) {
39236c6e 2714 return KERN_INVALID_NAME;
0a7de745 2715 }
39236c6e
A
2716
2717 kr = ipc_port_translate_receive(space, name, &port);
d9a64523
A
2718 if (kr != KERN_SUCCESS) {
2719 mach_port_guard_exception(name, 0, 0,
0a7de745
A
2720 ((KERN_INVALID_NAME == kr) ?
2721 kGUARD_EXC_INVALID_NAME :
2722 kGUARD_EXC_INVALID_RIGHT));
39236c6e 2723 return kr;
d9a64523 2724 }
39236c6e
A
2725
2726 /* Port locked and active */
2727 kr = mach_port_unguard_locked(port, name, guard);
2728 ip_unlock(port);
d9a64523 2729
39236c6e
A
2730 return kr;
2731}
cb323159
A
2732
2733/*
2734 * Routine: mach_port_guard_with_flags [kernel call]
2735 * Purpose:
2736 * Guard a mach port with specified guard value and guard flags.
2737 * The context field of the port is used as the guard.
2738 * Conditions:
2739 * Should hold receive right for that port
2740 * Returns:
2741 * KERN_SUCCESS The name is destroyed.
2742 * KERN_INVALID_TASK The space is null.
2743 * KERN_INVALID_TASK The space is dead.
2744 * KERN_INVALID_NAME The name doesn't denote a right.
2745 * KERN_INVALID_RIGHT The right isn't correct.
2746 * KERN_INVALID_ARGUMENT Port already contains a context/guard.
2747 * KERN_INVALID_CAPABILITY Cannot set MPG_IMMOVABLE_RECEIVE flag for a port with
2748 * a movable port-destroyed notification port
2749 */
2750kern_return_t
2751mach_port_guard_with_flags(
2752 ipc_space_t space,
2753 mach_port_name_t name,
2754 uint64_t guard,
2755 uint64_t flags)
2756{
2757 kern_return_t kr;
2758 ipc_port_t port;
2759
2760 if (space == IS_NULL) {
2761 return KERN_INVALID_TASK;
2762 }
2763
2764 if (!MACH_PORT_VALID(name)) {
2765 return KERN_INVALID_NAME;
2766 }
2767
2768 kr = ipc_port_translate_receive(space, name, &port);
2769 if (kr != KERN_SUCCESS) {
2770 mach_port_guard_exception(name, 0, 0,
2771 ((KERN_INVALID_NAME == kr) ?
2772 kGUARD_EXC_INVALID_NAME :
2773 kGUARD_EXC_INVALID_RIGHT));
2774 return kr;
2775 }
2776
2777 /* Port locked and active */
2778 kr = mach_port_guard_locked(port, guard, flags);
2779 ip_unlock(port);
2780
2781 if (KERN_INVALID_ARGUMENT == kr) {
2782 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_ARGUMENT);
2783 }
2784
2785 return kr;
2786}
2787
2788/*
2789 * Routine: mach_port_swap_guard [kernel call]
2790 * Purpose:
2791 * Swap guard value.
2792 * Conditions:
2793 * Port should already be guarded.
2794 * Returns:
2795 * KERN_SUCCESS The name is destroyed.
2796 * KERN_INVALID_TASK The space is null.
2797 * KERN_INVALID_TASK The space is dead.
2798 * KERN_INVALID_NAME The name doesn't denote a right.
2799 * KERN_INVALID_RIGHT The right isn't correct.
2800 * KERN_INVALID_ARGUMENT Port doesn't contain a guard; is strictly guarded
2801 * or the old_guard doesnt match the context
2802 */
2803kern_return_t
2804mach_port_swap_guard(
2805 ipc_space_t space,
2806 mach_port_name_t name,
2807 uint64_t old_guard,
2808 uint64_t new_guard)
2809{
2810 kern_return_t kr;
2811 ipc_port_t port;
2812
2813 if (space == IS_NULL) {
2814 return KERN_INVALID_TASK;
2815 }
2816
2817 if (!MACH_PORT_VALID(name)) {
2818 return KERN_INVALID_NAME;
2819 }
2820
2821 kr = ipc_port_translate_receive(space, name, &port);
2822 if (kr != KERN_SUCCESS) {
2823 mach_port_guard_exception(name, 0, 0,
2824 ((KERN_INVALID_NAME == kr) ?
2825 kGUARD_EXC_INVALID_NAME :
2826 kGUARD_EXC_INVALID_RIGHT));
2827 return kr;
2828 }
2829
2830 /* Port locked and active */
2831 if (!port->ip_guarded) {
2832 ip_unlock(port);
2833 mach_port_guard_exception(name, old_guard, 0, kGUARD_EXC_UNGUARDED);
2834 return KERN_INVALID_ARGUMENT;
2835 }
2836
2837 if (port->ip_strict_guard) {
2838 uint64_t portguard = port->ip_context;
2839 ip_unlock(port);
2840 /* For strictly guarded ports, disallow overwriting context; Raise Exception */
2841 mach_port_guard_exception(name, old_guard, portguard, kGUARD_EXC_SET_CONTEXT);
2842 return KERN_INVALID_ARGUMENT;
2843 }
2844
2845 if (port->ip_context != old_guard) {
2846 uint64_t portguard = port->ip_context;
2847 ip_unlock(port);
2848 mach_port_guard_exception(name, old_guard, portguard, kGUARD_EXC_INCORRECT_GUARD);
2849 return KERN_INVALID_ARGUMENT;
2850 }
2851
2852 imq_lock(&port->ip_messages);
2853 port->ip_context = new_guard;
2854 imq_unlock(&port->ip_messages);
2855
2856 ip_unlock(port);
2857
2858 return KERN_SUCCESS;
2859}