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