]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/mach_port.c
xnu-7195.101.1.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>
91447636 83#include <kern/thread.h>
5ba3f43e 84#include <kern/exc_guard.h>
1c79356b
A
85#include <mach/mach_port_server.h>
86#include <vm/vm_map.h>
87#include <vm/vm_kern.h>
d9a64523 88#include <ipc/port.h>
1c79356b
A
89#include <ipc/ipc_entry.h>
90#include <ipc/ipc_space.h>
91#include <ipc/ipc_object.h>
92#include <ipc/ipc_notify.h>
93#include <ipc/ipc_port.h>
94#include <ipc/ipc_pset.h>
95#include <ipc/ipc_right.h>
96#include <ipc/ipc_kmsg.h>
97#include <kern/misc_protos.h>
2d21ac55 98#include <security/mac_mach_internal.h>
f427ee49
A
99#include <kern/work_interval.h>
100#include <kern/policy_internal.h>
2d21ac55 101
fe8ab488
A
102#if IMPORTANCE_INHERITANCE
103#include <ipc/ipc_importance.h>
104#endif
1c79356b 105
f427ee49
A
106kern_return_t mach_port_get_attributes(ipc_space_t space, mach_port_name_t name,
107 int flavor, mach_port_info_t info, mach_msg_type_number_t *count);
108kern_return_t mach_port_get_context(ipc_space_t space, mach_port_name_t name,
109 mach_vm_address_t *context);
110kern_return_t mach_port_get_set_status(ipc_space_t space, mach_port_name_t name,
111 mach_port_name_t **members, mach_msg_type_number_t *membersCnt);
3e170ce0 112
1c79356b
A
113/* Zeroed template of qos flags */
114
0a7de745 115static mach_port_qos_t qos_template;
1c79356b
A
116
117/*
118 * Routine: mach_port_names_helper
119 * Purpose:
120 * A helper function for mach_port_names.
6d2010ae
A
121 *
122 * Conditions:
123 * Space containing entry is [at least] read-locked.
1c79356b 124 */
cb323159 125static void
1c79356b 126mach_port_names_helper(
0a7de745
A
127 ipc_port_timestamp_t timestamp,
128 ipc_entry_t entry,
129 mach_port_name_t name,
130 mach_port_name_t *names,
131 mach_port_type_t *types,
132 ipc_entry_num_t *actualp)
1c79356b
A
133{
134 ipc_entry_bits_t bits;
135 ipc_port_request_index_t request;
6d2010ae 136 mach_port_type_t type = 0;
1c79356b 137 ipc_entry_num_t actual;
6d2010ae 138 ipc_port_t port;
1c79356b
A
139
140 bits = entry->ie_bits;
141 request = entry->ie_request;
cb323159 142 port = ip_object_to_port(entry->ie_object);
1c79356b 143
6d2010ae
A
144 if (bits & MACH_PORT_TYPE_RECEIVE) {
145 assert(IP_VALID(port));
1c79356b 146
6d2010ae
A
147 if (request != IE_REQ_NONE) {
148 ip_lock(port);
cb323159 149 require_ip_active(port);
6d2010ae
A
150 type |= ipc_port_request_type(port, name, request);
151 ip_unlock(port);
152 }
6d2010ae
A
153 } else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
154 mach_port_type_t reqtype;
1c79356b 155
6d2010ae
A
156 assert(IP_VALID(port));
157 ip_lock(port);
1c79356b 158
6d2010ae 159 reqtype = (request != IE_REQ_NONE) ?
0a7de745
A
160 ipc_port_request_type(port, name, request) : 0;
161
6d2010ae
A
162 /*
163 * If the port is alive, or was alive when the mach_port_names
164 * started, then return that fact. Otherwise, pretend we found
165 * a dead name entry.
166 */
167 if (ip_active(port) || IP_TIMESTAMP_ORDER(timestamp, port->ip_timestamp)) {
168 type |= reqtype;
169 } else {
1c79356b
A
170 bits &= ~(IE_BITS_TYPE_MASK);
171 bits |= MACH_PORT_TYPE_DEAD_NAME;
6d2010ae 172 /* account for additional reference for dead-name notification */
0a7de745 173 if (reqtype != 0) {
1c79356b 174 bits++;
0a7de745 175 }
1c79356b 176 }
6d2010ae 177 ip_unlock(port);
1c79356b
A
178 }
179
6d2010ae 180 type |= IE_BITS_TYPE(bits);
1c79356b
A
181
182 actual = *actualp;
183 names[actual] = name;
184 types[actual] = type;
0a7de745 185 *actualp = actual + 1;
1c79356b
A
186}
187
188/*
189 * Routine: mach_port_names [kernel call]
190 * Purpose:
191 * Retrieves a list of the rights present in the space,
192 * along with type information. (Same as returned
193 * by mach_port_type.) The names are returned in
194 * no particular order, but they (and the type info)
195 * are an accurate snapshot of the space.
196 * Conditions:
197 * Nothing locked.
198 * Returns:
199 * KERN_SUCCESS Arrays of names and types returned.
200 * KERN_INVALID_TASK The space is null.
201 * KERN_INVALID_TASK The space is dead.
202 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
203 */
204
205kern_return_t
206mach_port_names(
0a7de745
A
207 ipc_space_t space,
208 mach_port_name_t **namesp,
209 mach_msg_type_number_t *namesCnt,
210 mach_port_type_t **typesp,
211 mach_msg_type_number_t *typesCnt)
1c79356b 212{
1c79356b
A
213 ipc_entry_t table;
214 ipc_entry_num_t tsize;
215 mach_port_index_t index;
0a7de745
A
216 ipc_entry_num_t actual; /* this many names */
217 ipc_port_timestamp_t timestamp; /* logical time of this operation */
1c79356b
A
218 mach_port_name_t *names;
219 mach_port_type_t *types;
220 kern_return_t kr;
221
0a7de745
A
222 vm_size_t size; /* size of allocated memory */
223 vm_offset_t addr1; /* allocated memory, for names */
224 vm_offset_t addr2; /* allocated memory, for types */
225 vm_map_copy_t memory1; /* copied-in memory, for names */
226 vm_map_copy_t memory2; /* copied-in memory, for types */
1c79356b
A
227
228 /* safe simplifying assumption */
39037602 229 static_assert(sizeof(mach_port_name_t) == sizeof(mach_port_type_t));
1c79356b 230
0a7de745 231 if (space == IS_NULL) {
1c79356b 232 return KERN_INVALID_TASK;
0a7de745 233 }
1c79356b
A
234
235 size = 0;
236
237 for (;;) {
238 ipc_entry_num_t bound;
239 vm_size_t size_needed;
240
241 is_read_lock(space);
316670eb 242 if (!is_active(space)) {
1c79356b
A
243 is_read_unlock(space);
244 if (size != 0) {
245 kmem_free(ipc_kernel_map, addr1, size);
246 kmem_free(ipc_kernel_map, addr2, size);
247 }
248 return KERN_INVALID_TASK;
249 }
250
251 /* upper bound on number of names in the space */
316670eb 252 bound = space->is_table_size;
39236c6e
A
253 size_needed = vm_map_round_page(
254 (bound * sizeof(mach_port_name_t)),
255 VM_MAP_PAGE_MASK(ipc_kernel_map));
1c79356b 256
0a7de745 257 if (size_needed <= size) {
1c79356b 258 break;
0a7de745 259 }
1c79356b
A
260
261 is_read_unlock(space);
262
263 if (size != 0) {
264 kmem_free(ipc_kernel_map, addr1, size);
265 kmem_free(ipc_kernel_map, addr2, size);
266 }
267 size = size_needed;
268
5ba3f43e 269 kr = vm_allocate_kernel(ipc_kernel_map, &addr1, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC);
0a7de745 270 if (kr != KERN_SUCCESS) {
1c79356b 271 return KERN_RESOURCE_SHORTAGE;
0a7de745 272 }
1c79356b 273
5ba3f43e 274 kr = vm_allocate_kernel(ipc_kernel_map, &addr2, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC);
1c79356b
A
275 if (kr != KERN_SUCCESS) {
276 kmem_free(ipc_kernel_map, addr1, size);
277 return KERN_RESOURCE_SHORTAGE;
278 }
279
280 /* can't fault while we hold locks */
281
5ba3f43e 282 kr = vm_map_wire_kernel(
39236c6e
A
283 ipc_kernel_map,
284 vm_map_trunc_page(addr1,
0a7de745 285 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 286 vm_map_round_page(addr1 + size,
0a7de745
A
287 VM_MAP_PAGE_MASK(ipc_kernel_map)),
288 VM_PROT_READ | VM_PROT_WRITE, VM_KERN_MEMORY_IPC,
39236c6e 289 FALSE);
9bccf70c
A
290 if (kr != KERN_SUCCESS) {
291 kmem_free(ipc_kernel_map, addr1, size);
292 kmem_free(ipc_kernel_map, addr2, size);
293 return KERN_RESOURCE_SHORTAGE;
294 }
1c79356b 295
5ba3f43e 296 kr = vm_map_wire_kernel(
39236c6e
A
297 ipc_kernel_map,
298 vm_map_trunc_page(addr2,
0a7de745 299 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 300 vm_map_round_page(addr2 + size,
0a7de745
A
301 VM_MAP_PAGE_MASK(ipc_kernel_map)),
302 VM_PROT_READ | VM_PROT_WRITE,
5ba3f43e 303 VM_KERN_MEMORY_IPC,
39236c6e 304 FALSE);
9bccf70c
A
305 if (kr != KERN_SUCCESS) {
306 kmem_free(ipc_kernel_map, addr1, size);
307 kmem_free(ipc_kernel_map, addr2, size);
308 return KERN_RESOURCE_SHORTAGE;
309 }
1c79356b
A
310 }
311 /* space is read-locked and active */
312
313 names = (mach_port_name_t *) addr1;
314 types = (mach_port_type_t *) addr2;
315 actual = 0;
316
317 timestamp = ipc_port_timestamp();
318
319 table = space->is_table;
320 tsize = space->is_table_size;
321
322 for (index = 0; index < tsize; index++) {
323 ipc_entry_t entry = &table[index];
324 ipc_entry_bits_t bits = entry->ie_bits;
325
326 if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) {
327 mach_port_name_t name;
328
329 name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
330 mach_port_names_helper(timestamp, entry, name, names,
0a7de745 331 types, &actual);
1c79356b
A
332 }
333 }
334
1c79356b
A
335 is_read_unlock(space);
336
337 if (actual == 0) {
338 memory1 = VM_MAP_COPY_NULL;
339 memory2 = VM_MAP_COPY_NULL;
340
341 if (size != 0) {
342 kmem_free(ipc_kernel_map, addr1, size);
343 kmem_free(ipc_kernel_map, addr2, size);
344 }
345 } else {
346 vm_size_t size_used;
347 vm_size_t vm_size_used;
348
349 size_used = actual * sizeof(mach_port_name_t);
39236c6e 350 vm_size_used =
0a7de745
A
351 vm_map_round_page(size_used,
352 VM_MAP_PAGE_MASK(ipc_kernel_map));
1c79356b
A
353
354 /*
355 * Make used memory pageable and get it into
356 * copied-in form. Free any unused memory.
357 */
358
39236c6e
A
359 kr = vm_map_unwire(
360 ipc_kernel_map,
361 vm_map_trunc_page(addr1,
0a7de745 362 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 363 vm_map_round_page(addr1 + vm_size_used,
0a7de745 364 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 365 FALSE);
1c79356b
A
366 assert(kr == KERN_SUCCESS);
367
39236c6e
A
368 kr = vm_map_unwire(
369 ipc_kernel_map,
370 vm_map_trunc_page(addr2,
0a7de745 371 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 372 vm_map_round_page(addr2 + vm_size_used,
0a7de745 373 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 374 FALSE);
1c79356b
A
375 assert(kr == KERN_SUCCESS);
376
91447636 377 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr1,
0a7de745 378 (vm_map_size_t)size_used, TRUE, &memory1);
1c79356b
A
379 assert(kr == KERN_SUCCESS);
380
91447636 381 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr2,
0a7de745 382 (vm_map_size_t)size_used, TRUE, &memory2);
1c79356b
A
383 assert(kr == KERN_SUCCESS);
384
385 if (vm_size_used != size) {
386 kmem_free(ipc_kernel_map,
0a7de745 387 addr1 + vm_size_used, size - vm_size_used);
1c79356b 388 kmem_free(ipc_kernel_map,
0a7de745 389 addr2 + vm_size_used, size - vm_size_used);
1c79356b
A
390 }
391 }
392
393 *namesp = (mach_port_name_t *) memory1;
394 *namesCnt = actual;
395 *typesp = (mach_port_type_t *) memory2;
396 *typesCnt = actual;
397 return KERN_SUCCESS;
398}
399
400/*
401 * Routine: mach_port_type [kernel call]
402 * Purpose:
403 * Retrieves the type of a right in the space.
404 * The type is a bitwise combination of one or more
405 * of the following type bits:
406 * MACH_PORT_TYPE_SEND
407 * MACH_PORT_TYPE_RECEIVE
408 * MACH_PORT_TYPE_SEND_ONCE
409 * MACH_PORT_TYPE_PORT_SET
410 * MACH_PORT_TYPE_DEAD_NAME
411 * In addition, the following pseudo-type bits may be present:
412 * MACH_PORT_TYPE_DNREQUEST
413 * A dead-name notification is requested.
414 * Conditions:
415 * Nothing locked.
416 * Returns:
417 * KERN_SUCCESS Type is returned.
418 * KERN_INVALID_TASK The space is null.
419 * KERN_INVALID_TASK The space is dead.
420 * KERN_INVALID_NAME The name doesn't denote a right.
421 */
422
423kern_return_t
424mach_port_type(
0a7de745
A
425 ipc_space_t space,
426 mach_port_name_t name,
427 mach_port_type_t *typep)
1c79356b
A
428{
429 mach_port_urefs_t urefs;
430 ipc_entry_t entry;
431 kern_return_t kr;
432
0a7de745 433 if (space == IS_NULL) {
1c79356b 434 return KERN_INVALID_TASK;
0a7de745 435 }
1c79356b 436
0a7de745 437 if (name == MACH_PORT_NULL) {
1c79356b 438 return KERN_INVALID_NAME;
0a7de745 439 }
1c79356b
A
440
441 if (name == MACH_PORT_DEAD) {
442 *typep = MACH_PORT_TYPE_DEAD_NAME;
443 return KERN_SUCCESS;
444 }
445
446 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523 447 if (kr != KERN_SUCCESS) {
1c79356b 448 return kr;
d9a64523 449 }
1c79356b 450
316670eb 451 /* space is write-locked and active */
1c79356b 452 kr = ipc_right_info(space, name, entry, typep, &urefs);
316670eb
A
453 /* space is unlocked */
454
6d2010ae 455#if 1
0a7de745
A
456 /* JMM - workaround rdar://problem/9121297 (CF being too picky on these bits). */
457 *typep &= ~(MACH_PORT_TYPE_SPREQUEST | MACH_PORT_TYPE_SPREQUEST_DELAYED);
6d2010ae
A
458#endif
459
1c79356b
A
460 return kr;
461}
462
463/*
464 * Routine: mach_port_rename [kernel call]
465 * Purpose:
466 * Changes the name denoting a right,
467 * from oname to nname.
468 * Conditions:
469 * Nothing locked.
470 * Returns:
471 * KERN_SUCCESS The right is renamed.
472 * KERN_INVALID_TASK The space is null.
473 * KERN_INVALID_TASK The space is dead.
474 * KERN_INVALID_NAME The oname doesn't denote a right.
475 * KERN_INVALID_VALUE The nname isn't a legal name.
476 * KERN_NAME_EXISTS The nname already denotes a right.
477 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
316670eb
A
478 *
479 * This interface is obsolete and always returns
480 * KERN_NOT_SUPPORTED.
1c79356b
A
481 */
482
483kern_return_t
484mach_port_rename(
0a7de745
A
485 __unused ipc_space_t space,
486 __unused mach_port_name_t oname,
487 __unused mach_port_name_t nname)
1c79356b 488{
316670eb 489 return KERN_NOT_SUPPORTED;
1c79356b
A
490}
491
316670eb 492
1c79356b
A
493/*
494 * Routine: mach_port_allocate_name [kernel call]
495 * Purpose:
496 * Allocates a right in a space, using a specific name
497 * for the new right. Possible rights:
498 * MACH_PORT_RIGHT_RECEIVE
499 * MACH_PORT_RIGHT_PORT_SET
500 * MACH_PORT_RIGHT_DEAD_NAME
501 *
502 * A new port (allocated with MACH_PORT_RIGHT_RECEIVE)
503 * has no extant send or send-once rights and no queued
504 * messages. Its queue limit is MACH_PORT_QLIMIT_DEFAULT
505 * and its make-send count is 0. It is not a member of
506 * a port set. It has no registered no-senders or
507 * port-destroyed notification requests.
508 *
509 * A new port set has no members.
510 *
511 * A new dead name has one user reference.
512 * Conditions:
513 * Nothing locked.
514 * Returns:
515 * KERN_SUCCESS The right is allocated.
516 * KERN_INVALID_TASK The space is null.
517 * KERN_INVALID_TASK The space is dead.
518 * KERN_INVALID_VALUE The name isn't a legal name.
519 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
520 * KERN_NAME_EXISTS The name already denotes a right.
521 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
522 *
523 * Restrictions on name allocation: NT bits are reserved by kernel,
524 * must be set on any chosen name. Can't do this at all in kernel
525 * loaded server.
526 */
527
528kern_return_t
529mach_port_allocate_name(
0a7de745
A
530 ipc_space_t space,
531 mach_port_right_t right,
532 mach_port_name_t name)
1c79356b 533{
0a7de745
A
534 kern_return_t kr;
535 mach_port_qos_t qos = qos_template;
1c79356b
A
536
537 qos.name = TRUE;
538
0a7de745 539 if (!MACH_PORT_VALID(name)) {
1c79356b 540 return KERN_INVALID_VALUE;
0a7de745 541 }
1c79356b 542
0a7de745
A
543 kr = mach_port_allocate_full(space, right, MACH_PORT_NULL,
544 &qos, &name);
545 return kr;
1c79356b
A
546}
547
548/*
549 * Routine: mach_port_allocate [kernel call]
550 * Purpose:
551 * Allocates a right in a space. Like mach_port_allocate_name,
552 * except that the implementation picks a name for the right.
553 * The name may be any legal name in the space that doesn't
554 * currently denote a right.
555 * Conditions:
556 * Nothing locked.
557 * Returns:
558 * KERN_SUCCESS The right is allocated.
559 * KERN_INVALID_TASK The space is null.
560 * KERN_INVALID_TASK The space is dead.
561 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
562 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
563 * KERN_NO_SPACE No room in space for another right.
564 */
565
566kern_return_t
567mach_port_allocate(
0a7de745
A
568 ipc_space_t space,
569 mach_port_right_t right,
570 mach_port_name_t *namep)
1c79356b 571{
0a7de745
A
572 kern_return_t kr;
573 mach_port_qos_t qos = qos_template;
1c79356b 574
0a7de745
A
575 kr = mach_port_allocate_full(space, right, MACH_PORT_NULL,
576 &qos, namep);
577 return kr;
1c79356b
A
578}
579
580/*
581 * Routine: mach_port_allocate_qos [kernel call]
582 * Purpose:
0a7de745
A
583 * Allocates a right, with qos options, in a space. Like
584 * mach_port_allocate_name, except that the implementation
585 * picks a name for the right. The name may be any legal name
1c79356b
A
586 * in the space that doesn't currently denote a right.
587 * Conditions:
588 * Nothing locked.
589 * Returns:
590 * KERN_SUCCESS The right is allocated.
591 * KERN_INVALID_TASK The space is null.
592 * KERN_INVALID_TASK The space is dead.
593 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
594 * KERN_INVALID_ARGUMENT The qos request was invalid.
595 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
596 * KERN_NO_SPACE No room in space for another right.
597 */
598
599kern_return_t
600mach_port_allocate_qos(
0a7de745
A
601 ipc_space_t space,
602 mach_port_right_t right,
603 mach_port_qos_t *qosp,
604 mach_port_name_t *namep)
1c79356b 605{
0a7de745 606 kern_return_t kr;
1c79356b 607
0a7de745 608 if (qosp->name) {
1c79356b 609 return KERN_INVALID_ARGUMENT;
0a7de745
A
610 }
611 kr = mach_port_allocate_full(space, right, MACH_PORT_NULL,
612 qosp, namep);
613 return kr;
1c79356b
A
614}
615
1c79356b
A
616/*
617 * Routine: mach_port_allocate_full [kernel call]
618 * Purpose:
cb323159
A
619 * Allocates a right in a space. Supports the
620 * special case of specifying a name. The name may
621 * be any legal name in the space that doesn't
1c79356b 622 * currently denote a right.
cb323159
A
623 *
624 * While we no longer support users requesting
625 * preallocated message for the port, we still
626 * check for errors in such requests and then
627 * just clear the request.
1c79356b
A
628 * Conditions:
629 * Nothing locked.
630 * Returns:
631 * KERN_SUCCESS The right is allocated.
632 * KERN_INVALID_TASK The space is null.
633 * KERN_INVALID_TASK The space is dead.
634 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
635 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
636 * KERN_NO_SPACE No room in space for another right.
637 */
638
639kern_return_t
640mach_port_allocate_full(
0a7de745
A
641 ipc_space_t space,
642 mach_port_right_t right,
643 mach_port_t proto,
644 mach_port_qos_t *qosp,
645 mach_port_name_t *namep)
1c79356b 646{
0a7de745 647 kern_return_t kr;
1c79356b 648
0a7de745
A
649 if (space == IS_NULL) {
650 return KERN_INVALID_TASK;
651 }
1c79356b 652
0a7de745
A
653 if (proto != MACH_PORT_NULL) {
654 return KERN_INVALID_VALUE;
655 }
9bccf70c 656
1c79356b 657 if (qosp->name) {
0a7de745
A
658 if (!MACH_PORT_VALID(*namep)) {
659 return KERN_INVALID_VALUE;
660 }
1c79356b
A
661 }
662
cb323159
A
663 /*
664 * Don't actually honor prealloc requests from user-space
665 * (for security reasons, and because it isn't guaranteed anyway).
666 * Keep old errors for legacy reasons.
667 */
1c79356b 668 if (qosp->prealloc) {
8ad349bb
A
669 if (qosp->len > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE) {
670 return KERN_RESOURCE_SHORTAGE;
8ad349bb 671 }
cb323159
A
672 if (right != MACH_PORT_RIGHT_RECEIVE) {
673 return KERN_INVALID_VALUE;
674 }
675 qosp->prealloc = 0;
1c79356b
A
676 }
677
cb323159
A
678 kr = mach_port_allocate_internal(space, right, qosp, namep);
679 return kr;
680}
681
682
683/*
684 * Routine: mach_port_allocate_internal [kernel private]
685 * Purpose:
686 * Allocates a right in a space. Supports all of the
687 * special cases, a specific name, a real-time port, etc.
688 * The name may be any legal name in the space that doesn't
689 * currently denote a right.
690 * Conditions:
691 * Nothing locked.
692 * Returns:
693 * KERN_SUCCESS The right is allocated.
694 * KERN_INVALID_TASK The space is null.
695 * KERN_INVALID_TASK The space is dead.
696 * KERN_INVALID_VALUE "right" isn't a legal kind of right.
697 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
698 * KERN_NO_SPACE No room in space for another right.
699 */
700kern_return_t
701mach_port_allocate_internal(
702 ipc_space_t space,
703 mach_port_right_t right,
704 mach_port_qos_t *qosp,
705 mach_port_name_t *namep)
706{
707 kern_return_t kr;
708
709 assert(space != IS_NULL);
710
1c79356b 711 switch (right) {
0a7de745
A
712 case MACH_PORT_RIGHT_RECEIVE:
713 {
cb323159 714 ipc_kmsg_t kmsg = IKM_NULL;
0a7de745 715 ipc_port_t port;
1c79356b 716
cb323159
A
717 /*
718 * For in-kernel uses, only allow small (from the kmsg zone)
719 * preallocated messages for the port.
720 */
721 if (qosp->prealloc) {
722 mach_msg_size_t size = qosp->len;
723
724 if (size > IKM_SAVED_MSG_SIZE - MAX_TRAILER_SIZE) {
725 panic("mach_port_allocate_internal: too large a prealloc kmsg");
726 }
727 kmsg = (ipc_kmsg_t)ipc_kmsg_prealloc(size + MAX_TRAILER_SIZE);
728 if (kmsg == IKM_NULL) {
729 return KERN_RESOURCE_SHORTAGE;
730 }
731 }
732
0a7de745 733 if (qosp->name) {
94ff46dc
A
734 kr = ipc_port_alloc_name(space, IPC_PORT_INIT_MESSAGE_QUEUE,
735 *namep, &port);
0a7de745 736 } else {
94ff46dc
A
737 kr = ipc_port_alloc(space, IPC_PORT_INIT_MESSAGE_QUEUE,
738 namep, &port);
0a7de745 739 }
1c79356b 740 if (kr == KERN_SUCCESS) {
0a7de745 741 if (kmsg != IKM_NULL) {
1c79356b 742 ipc_kmsg_set_prealloc(kmsg, port);
0a7de745 743 }
1c79356b 744 ip_unlock(port);
0a7de745 745 } else if (kmsg != IKM_NULL) {
1c79356b 746 ipc_kmsg_free(kmsg);
0a7de745 747 }
1c79356b 748 break;
0a7de745 749 }
1c79356b 750
0a7de745
A
751 case MACH_PORT_RIGHT_PORT_SET:
752 {
753 ipc_pset_t pset;
1c79356b 754
0a7de745 755 if (qosp->name) {
1c79356b 756 kr = ipc_pset_alloc_name(space, *namep, &pset);
0a7de745 757 } else {
1c79356b 758 kr = ipc_pset_alloc(space, namep, &pset);
0a7de745
A
759 }
760 if (kr == KERN_SUCCESS) {
1c79356b 761 ips_unlock(pset);
0a7de745 762 }
1c79356b 763 break;
0a7de745 764 }
1c79356b 765
0a7de745 766 case MACH_PORT_RIGHT_DEAD_NAME:
1c79356b
A
767 kr = ipc_object_alloc_dead(space, namep);
768 break;
769
0a7de745 770 default:
1c79356b
A
771 kr = KERN_INVALID_VALUE;
772 break;
773 }
774
0a7de745 775 return kr;
1c79356b
A
776}
777
778/*
779 * Routine: mach_port_destroy [kernel call]
780 * Purpose:
781 * Cleans up and destroys all rights denoted by a name
782 * in a space. The destruction of a receive right
783 * destroys the port, unless a port-destroyed request
784 * has been made for it; the destruction of a port-set right
785 * destroys the port set.
786 * Conditions:
787 * Nothing locked.
788 * Returns:
789 * KERN_SUCCESS The name is destroyed.
790 * KERN_INVALID_TASK The space is null.
791 * KERN_INVALID_TASK The space is dead.
792 * KERN_INVALID_NAME The name doesn't denote a right.
793 */
794
795kern_return_t
796mach_port_destroy(
0a7de745
A
797 ipc_space_t space,
798 mach_port_name_t name)
1c79356b
A
799{
800 ipc_entry_t entry;
801 kern_return_t kr;
802
0a7de745 803 if (space == IS_NULL) {
1c79356b 804 return KERN_INVALID_TASK;
0a7de745 805 }
1c79356b 806
0a7de745 807 if (!MACH_PORT_VALID(name)) {
1c79356b 808 return KERN_SUCCESS;
0a7de745 809 }
1c79356b
A
810
811 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
812 if (kr != KERN_SUCCESS) {
813 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b 814 return kr;
d9a64523 815 }
1c79356b
A
816 /* space is write-locked and active */
817
39236c6e 818 kr = ipc_right_destroy(space, name, entry, TRUE, 0); /* unlocks space */
1c79356b
A
819 return kr;
820}
821
822/*
823 * Routine: mach_port_deallocate [kernel call]
824 * Purpose:
825 * Deallocates a user reference from a send right,
5ba3f43e
A
826 * send-once right, dead-name right or a port_set right.
827 * May deallocate the right, if this is the last uref,
1c79356b
A
828 * and destroy the name, if it doesn't denote
829 * other rights.
830 * Conditions:
831 * Nothing locked.
832 * Returns:
833 * KERN_SUCCESS The uref is deallocated.
834 * KERN_INVALID_TASK The space is null.
835 * KERN_INVALID_TASK The space is dead.
836 * KERN_INVALID_NAME The name doesn't denote a right.
837 * KERN_INVALID_RIGHT The right isn't correct.
838 */
839
840kern_return_t
841mach_port_deallocate(
0a7de745
A
842 ipc_space_t space,
843 mach_port_name_t name)
1c79356b
A
844{
845 ipc_entry_t entry;
846 kern_return_t kr;
847
0a7de745 848 if (space == IS_NULL) {
1c79356b 849 return KERN_INVALID_TASK;
0a7de745 850 }
1c79356b 851
0a7de745 852 if (!MACH_PORT_VALID(name)) {
1c79356b 853 return KERN_SUCCESS;
0a7de745 854 }
1c79356b
A
855
856 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
857 if (kr != KERN_SUCCESS) {
858 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b 859 return kr;
d9a64523 860 }
1c79356b
A
861 /* space is write-locked */
862
863 kr = ipc_right_dealloc(space, name, entry); /* unlocks space */
864 return kr;
865}
866
867/*
868 * Routine: mach_port_get_refs [kernel call]
869 * Purpose:
870 * Retrieves the number of user references held by a right.
871 * Receive rights, port-set rights, and send-once rights
872 * always have one user reference. Returns zero if the
873 * name denotes a right, but not the queried right.
874 * Conditions:
875 * Nothing locked.
876 * Returns:
877 * KERN_SUCCESS Number of urefs returned.
878 * KERN_INVALID_TASK The space is null.
879 * KERN_INVALID_TASK The space is dead.
880 * KERN_INVALID_VALUE "right" isn't a legal value.
881 * KERN_INVALID_NAME The name doesn't denote a right.
882 */
883
884kern_return_t
885mach_port_get_refs(
0a7de745
A
886 ipc_space_t space,
887 mach_port_name_t name,
888 mach_port_right_t right,
889 mach_port_urefs_t *urefsp)
1c79356b
A
890{
891 mach_port_type_t type;
892 mach_port_urefs_t urefs;
893 ipc_entry_t entry;
894 kern_return_t kr;
895
0a7de745 896 if (space == IS_NULL) {
1c79356b 897 return KERN_INVALID_TASK;
0a7de745 898 }
1c79356b 899
0a7de745 900 if (right >= MACH_PORT_RIGHT_NUMBER) {
1c79356b 901 return KERN_INVALID_VALUE;
0a7de745 902 }
1c79356b
A
903
904 if (!MACH_PORT_VALID(name)) {
0a7de745 905 if (right == MACH_PORT_RIGHT_SEND ||
1c79356b
A
906 right == MACH_PORT_RIGHT_SEND_ONCE) {
907 *urefsp = 1;
908 return KERN_SUCCESS;
909 }
910 return KERN_INVALID_NAME;
911 }
912
913 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523 914 if (kr != KERN_SUCCESS) {
1c79356b 915 return kr;
d9a64523 916 }
316670eb 917
1c79356b 918 /* space is write-locked and active */
316670eb
A
919 kr = ipc_right_info(space, name, entry, &type, &urefs);
920 /* space is unlocked */
1c79356b 921
0a7de745
A
922 if (kr != KERN_SUCCESS) {
923 return kr;
924 }
1c79356b 925
0a7de745 926 if (type & MACH_PORT_TYPE(right)) {
1c79356b 927 switch (right) {
0a7de745 928 case MACH_PORT_RIGHT_SEND_ONCE:
1c79356b 929 assert(urefs == 1);
f427ee49 930 OS_FALLTHROUGH;
1c79356b 931
0a7de745
A
932 case MACH_PORT_RIGHT_PORT_SET:
933 case MACH_PORT_RIGHT_RECEIVE:
1c79356b
A
934 *urefsp = 1;
935 break;
936
0a7de745
A
937 case MACH_PORT_RIGHT_DEAD_NAME:
938 case MACH_PORT_RIGHT_SEND:
1c79356b
A
939 assert(urefs > 0);
940 *urefsp = urefs;
941 break;
942
0a7de745 943 default:
1c79356b
A
944 panic("mach_port_get_refs: strange rights");
945 }
0a7de745 946 } else {
1c79356b 947 *urefsp = 0;
0a7de745 948 }
1c79356b
A
949
950 return kr;
951}
952
953/*
954 * Routine: mach_port_mod_refs
955 * Purpose:
956 * Modifies the number of user references held by a right.
957 * The resulting number of user references must be non-negative.
958 * If it is zero, the right is deallocated. If the name
959 * doesn't denote other rights, it is destroyed.
960 * Conditions:
961 * Nothing locked.
962 * Returns:
963 * KERN_SUCCESS Modified number of urefs.
964 * KERN_INVALID_TASK The space is null.
965 * KERN_INVALID_TASK The space is dead.
966 * KERN_INVALID_VALUE "right" isn't a legal value.
967 * KERN_INVALID_NAME The name doesn't denote a right.
968 * KERN_INVALID_RIGHT Name doesn't denote specified right.
969 * KERN_INVALID_VALUE Impossible modification to urefs.
970 * KERN_UREFS_OVERFLOW Urefs would overflow.
971 */
972
973kern_return_t
974mach_port_mod_refs(
0a7de745
A
975 ipc_space_t space,
976 mach_port_name_t name,
977 mach_port_right_t right,
978 mach_port_delta_t delta)
1c79356b
A
979{
980 ipc_entry_t entry;
981 kern_return_t kr;
982
0a7de745 983 if (space == IS_NULL) {
1c79356b 984 return KERN_INVALID_TASK;
0a7de745 985 }
1c79356b 986
0a7de745 987 if (right >= MACH_PORT_RIGHT_NUMBER) {
1c79356b 988 return KERN_INVALID_VALUE;
0a7de745 989 }
1c79356b
A
990
991 if (!MACH_PORT_VALID(name)) {
992 if (right == MACH_PORT_RIGHT_SEND ||
0a7de745 993 right == MACH_PORT_RIGHT_SEND_ONCE) {
1c79356b 994 return KERN_SUCCESS;
0a7de745 995 }
1c79356b
A
996 return KERN_INVALID_NAME;
997 }
998
999 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
1000 if (kr != KERN_SUCCESS) {
1001 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
1c79356b 1002 return kr;
d9a64523
A
1003 }
1004
1c79356b
A
1005 /* space is write-locked and active */
1006
0a7de745 1007 kr = ipc_right_delta(space, name, entry, right, delta); /* unlocks */
1c79356b
A
1008 return kr;
1009}
1010
1011
39236c6e
A
1012/*
1013 * Routine: mach_port_peek [kernel call]
1014 * Purpose:
1015 * Peek at the message queue for the specified receive
1016 * right and return info about a message in the queue.
1017 *
1018 * On input, seqnop points to a sequence number value
1019 * to match the message being peeked. If zero is specified
1020 * as the seqno, the first message in the queue will be
1021 * peeked.
1022 *
1023 * Only the following trailer types are currently supported:
1024 * MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)
1025 *
1026 * or'ed with one of these element types:
1027 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_NULL)
1028 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SEQNO)
1029 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER)
1030 * MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)
1031 *
1032 * On input, the value pointed to by trailer_sizep must be
1033 * large enough to hold the requested trailer size.
1034 *
1035 * The message sequence number, id, size, requested trailer info
1036 * and requested trailer size are returned in their respective
1037 * output parameters upon success.
1038 *
1039 * Conditions:
1040 * Nothing locked.
1041 * Returns:
1042 * KERN_SUCCESS Matching message found, out parameters set.
1043 * KERN_INVALID_TASK The space is null or dead.
1044 * KERN_INVALID_NAME The name doesn't denote a right.
1045 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1046 * KERN_INVALID_VALUE The input parameter values are out of bounds.
1047 * KERN_FAILURE The requested message was not found.
1048 */
1049
1050kern_return_t
1051mach_port_peek(
0a7de745
A
1052 ipc_space_t space,
1053 mach_port_name_t name,
1054 mach_msg_trailer_type_t trailer_type,
1055 mach_port_seqno_t *seqnop,
1056 mach_msg_size_t *msg_sizep,
1057 mach_msg_id_t *msg_idp,
1058 mach_msg_trailer_info_t trailer_infop,
1059 mach_msg_type_number_t *trailer_sizep)
39236c6e
A
1060{
1061 ipc_port_t port;
1062 kern_return_t kr;
1063 boolean_t found;
1064 mach_msg_max_trailer_t max_trailer;
1065
0a7de745 1066 if (space == IS_NULL) {
39236c6e 1067 return KERN_INVALID_TASK;
0a7de745 1068 }
39236c6e 1069
0a7de745 1070 if (!MACH_PORT_VALID(name)) {
39236c6e 1071 return KERN_INVALID_RIGHT;
0a7de745 1072 }
39236c6e
A
1073
1074 /*
1075 * We don't allow anything greater than the audit trailer - to avoid
1076 * leaking the context pointer and to avoid variable-sized context issues.
1077 */
1078 if (GET_RCV_ELEMENTS(trailer_type) > MACH_RCV_TRAILER_AUDIT ||
d9a64523
A
1079 REQUESTED_TRAILER_SIZE(TRUE, trailer_type) > *trailer_sizep) {
1080 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_VALUE);
39236c6e 1081 return KERN_INVALID_VALUE;
d9a64523 1082 }
39236c6e
A
1083
1084 *trailer_sizep = REQUESTED_TRAILER_SIZE(TRUE, trailer_type);
1085
1086 kr = ipc_port_translate_receive(space, name, &port);
d9a64523
A
1087 if (kr != KERN_SUCCESS) {
1088 mach_port_guard_exception(name, 0, 0,
0a7de745
A
1089 ((KERN_INVALID_NAME == kr) ?
1090 kGUARD_EXC_INVALID_NAME :
1091 kGUARD_EXC_INVALID_RIGHT));
39236c6e 1092 return kr;
d9a64523 1093 }
39236c6e
A
1094
1095 /* Port locked and active */
1096
1097 found = ipc_mqueue_peek(&port->ip_messages, seqnop,
0a7de745 1098 msg_sizep, msg_idp, &max_trailer, NULL);
39236c6e
A
1099 ip_unlock(port);
1100
0a7de745 1101 if (found != TRUE) {
39236c6e 1102 return KERN_FAILURE;
0a7de745 1103 }
39236c6e
A
1104
1105 max_trailer.msgh_seqno = *seqnop;
1106 memcpy(trailer_infop, &max_trailer, *trailer_sizep);
1107
1108 return KERN_SUCCESS;
1109}
1110
1c79356b
A
1111/*
1112 * Routine: mach_port_set_mscount [kernel call]
1113 * Purpose:
1114 * Changes a receive right's make-send count.
1115 * Conditions:
1116 * Nothing locked.
1117 * Returns:
1118 * KERN_SUCCESS Set make-send count.
1119 * KERN_INVALID_TASK The space is null.
1120 * KERN_INVALID_TASK The space is dead.
1121 * KERN_INVALID_NAME The name doesn't denote a right.
1122 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1123 */
1124
1125kern_return_t
1126mach_port_set_mscount(
0a7de745
A
1127 ipc_space_t space,
1128 mach_port_name_t name,
1129 mach_port_mscount_t mscount)
1c79356b
A
1130{
1131 ipc_port_t port;
1132 kern_return_t kr;
1133
0a7de745 1134 if (space == IS_NULL) {
1c79356b 1135 return KERN_INVALID_TASK;
0a7de745 1136 }
1c79356b 1137
0a7de745 1138 if (!MACH_PORT_VALID(name)) {
1c79356b 1139 return KERN_INVALID_RIGHT;
0a7de745 1140 }
1c79356b
A
1141
1142 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1143 if (kr != KERN_SUCCESS) {
1c79356b 1144 return kr;
0a7de745 1145 }
1c79356b
A
1146 /* port is locked and active */
1147
cb323159 1148 port->ip_mscount = mscount;
1c79356b
A
1149 ip_unlock(port);
1150 return KERN_SUCCESS;
1151}
1152
1153/*
1154 * Routine: mach_port_set_seqno [kernel call]
1155 * Purpose:
1156 * Changes a receive right's sequence number.
1157 * Conditions:
1158 * Nothing locked.
1159 * Returns:
1160 * KERN_SUCCESS Set sequence number.
1161 * KERN_INVALID_TASK The space is null.
1162 * KERN_INVALID_TASK The space is dead.
1163 * KERN_INVALID_NAME The name doesn't denote a right.
1164 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1165 */
1166
1167kern_return_t
1168mach_port_set_seqno(
0a7de745
A
1169 ipc_space_t space,
1170 mach_port_name_t name,
1171 mach_port_seqno_t seqno)
1c79356b
A
1172{
1173 ipc_port_t port;
1174 kern_return_t kr;
1175
0a7de745 1176 if (space == IS_NULL) {
1c79356b 1177 return KERN_INVALID_TASK;
0a7de745 1178 }
1c79356b 1179
0a7de745 1180 if (!MACH_PORT_VALID(name)) {
1c79356b 1181 return KERN_INVALID_RIGHT;
0a7de745 1182 }
1c79356b
A
1183
1184 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1185 if (kr != KERN_SUCCESS) {
1c79356b 1186 return kr;
0a7de745 1187 }
1c79356b
A
1188 /* port is locked and active */
1189
1190 ipc_mqueue_set_seqno(&port->ip_messages, seqno);
1191
1192 ip_unlock(port);
1193 return KERN_SUCCESS;
1194}
1195
b0d623f7
A
1196/*
1197 * Routine: mach_port_get_context [kernel call]
1198 * Purpose:
1199 * Returns a receive right's context pointer.
1200 * Conditions:
1201 * Nothing locked.
1202 * Returns:
1203 * KERN_SUCCESS Set context pointer.
1204 * KERN_INVALID_TASK The space is null.
1205 * KERN_INVALID_TASK The space is dead.
1206 * KERN_INVALID_NAME The name doesn't denote a right.
1207 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1208 */
1209
1210kern_return_t
1211mach_port_get_context(
0a7de745
A
1212 ipc_space_t space,
1213 mach_port_name_t name,
1214 mach_vm_address_t *context)
b0d623f7
A
1215{
1216 ipc_port_t port;
1217 kern_return_t kr;
1218
0a7de745 1219 if (space == IS_NULL) {
b0d623f7 1220 return KERN_INVALID_TASK;
0a7de745 1221 }
b0d623f7 1222
0a7de745 1223 if (!MACH_PORT_VALID(name)) {
b0d623f7 1224 return KERN_INVALID_RIGHT;
0a7de745 1225 }
b0d623f7
A
1226
1227 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1228 if (kr != KERN_SUCCESS) {
b0d623f7 1229 return kr;
0a7de745 1230 }
b0d623f7 1231
39236c6e
A
1232 /* Port locked and active */
1233
1234 /* For strictly guarded ports, return empty context (which acts as guard) */
0a7de745 1235 if (port->ip_strict_guard) {
39236c6e 1236 *context = 0;
0a7de745 1237 } else {
39236c6e 1238 *context = port->ip_context;
0a7de745 1239 }
b0d623f7
A
1240
1241 ip_unlock(port);
1242 return KERN_SUCCESS;
1243}
1244
f427ee49
A
1245kern_return_t
1246mach_port_get_context_from_user(
1247 mach_port_t port,
1248 mach_port_name_t name,
1249 mach_vm_address_t *context)
1250{
1251 kern_return_t kr;
1252
1253 ipc_space_t space = convert_port_to_space_check_type(port, NULL, TASK_FLAVOR_READ, FALSE);
1254
1255 if (space == IPC_SPACE_NULL) {
1256 return KERN_INVALID_ARGUMENT;
1257 }
1258
1259 kr = mach_port_get_context(space, name, context);
1260
1261 ipc_space_release(space);
1262 return kr;
1263}
b0d623f7
A
1264
1265/*
1266 * Routine: mach_port_set_context [kernel call]
1267 * Purpose:
1268 * Changes a receive right's context pointer.
1269 * Conditions:
1270 * Nothing locked.
1271 * Returns:
1272 * KERN_SUCCESS Set context pointer.
1273 * KERN_INVALID_TASK The space is null.
1274 * KERN_INVALID_TASK The space is dead.
1275 * KERN_INVALID_NAME The name doesn't denote a right.
1276 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
1277 */
1278
1279kern_return_t
1280mach_port_set_context(
0a7de745
A
1281 ipc_space_t space,
1282 mach_port_name_t name,
1283 mach_vm_address_t context)
b0d623f7
A
1284{
1285 ipc_port_t port;
1286 kern_return_t kr;
1287
0a7de745 1288 if (space == IS_NULL) {
b0d623f7 1289 return KERN_INVALID_TASK;
0a7de745 1290 }
b0d623f7 1291
0a7de745 1292 if (!MACH_PORT_VALID(name)) {
b0d623f7 1293 return KERN_INVALID_RIGHT;
0a7de745 1294 }
b0d623f7
A
1295
1296 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1297 if (kr != KERN_SUCCESS) {
b0d623f7 1298 return kr;
0a7de745 1299 }
b0d623f7
A
1300
1301 /* port is locked and active */
0a7de745 1302 if (port->ip_strict_guard) {
39236c6e
A
1303 uint64_t portguard = port->ip_context;
1304 ip_unlock(port);
1305 /* For strictly guarded ports, disallow overwriting context; Raise Exception */
1306 mach_port_guard_exception(name, context, portguard, kGUARD_EXC_SET_CONTEXT);
1307 return KERN_INVALID_ARGUMENT;
1308 }
1309
b0d623f7
A
1310 port->ip_context = context;
1311
1312 ip_unlock(port);
1313 return KERN_SUCCESS;
1314}
1315
1316
1c79356b
A
1317/*
1318 * Routine: mach_port_get_set_status [kernel call]
1319 * Purpose:
1320 * Retrieves a list of members in a port set.
1321 * Returns the space's name for each receive right member.
1322 * Conditions:
1323 * Nothing locked.
1324 * Returns:
1325 * KERN_SUCCESS Retrieved list of members.
1326 * KERN_INVALID_TASK The space is null.
1327 * KERN_INVALID_TASK The space is dead.
1328 * KERN_INVALID_NAME The name doesn't denote a right.
1329 * KERN_INVALID_RIGHT Name doesn't denote a port set.
1330 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1331 */
1332
1333kern_return_t
1334mach_port_get_set_status(
0a7de745
A
1335 ipc_space_t space,
1336 mach_port_name_t name,
1337 mach_port_name_t **members,
1338 mach_msg_type_number_t *membersCnt)
1c79356b 1339{
0a7de745
A
1340 ipc_entry_num_t actual; /* this many members */
1341 ipc_entry_num_t maxnames; /* space for this many members */
1c79356b
A
1342 kern_return_t kr;
1343
0a7de745
A
1344 vm_size_t size; /* size of allocated memory */
1345 vm_offset_t addr; /* allocated memory */
1346 vm_map_copy_t memory; /* copied-in memory */
1c79356b 1347
0a7de745 1348 if (space == IS_NULL) {
1c79356b 1349 return KERN_INVALID_TASK;
0a7de745 1350 }
1c79356b 1351
0a7de745 1352 if (!MACH_PORT_VALID(name)) {
1c79356b 1353 return KERN_INVALID_RIGHT;
0a7de745 1354 }
1c79356b 1355
0a7de745 1356 size = VM_MAP_PAGE_SIZE(ipc_kernel_map); /* initial guess */
1c79356b
A
1357
1358 for (;;) {
1c79356b 1359 mach_port_name_t *names;
39236c6e 1360 ipc_object_t psobj;
1c79356b
A
1361 ipc_pset_t pset;
1362
5ba3f43e 1363 kr = vm_allocate_kernel(ipc_kernel_map, &addr, size, VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC);
0a7de745 1364 if (kr != KERN_SUCCESS) {
1c79356b 1365 return KERN_RESOURCE_SHORTAGE;
0a7de745 1366 }
1c79356b
A
1367
1368 /* can't fault while we hold locks */
1369
5ba3f43e 1370 kr = vm_map_wire_kernel(ipc_kernel_map, addr, addr + size,
0a7de745 1371 VM_PROT_READ | VM_PROT_WRITE, VM_KERN_MEMORY_IPC, FALSE);
1c79356b
A
1372 assert(kr == KERN_SUCCESS);
1373
39236c6e 1374 kr = ipc_object_translate(space, name, MACH_PORT_RIGHT_PORT_SET, &psobj);
1c79356b
A
1375 if (kr != KERN_SUCCESS) {
1376 kmem_free(ipc_kernel_map, addr, size);
1377 return kr;
1378 }
1c79356b 1379
39236c6e 1380 /* just use a portset reference from here on out */
cb323159 1381 pset = ips_object_to_pset(psobj);
39236c6e 1382 ips_reference(pset);
0a7de745 1383 ips_unlock(pset);
1c79356b
A
1384
1385 names = (mach_port_name_t *) addr;
b0d623f7 1386 maxnames = (ipc_entry_num_t)(size / sizeof(mach_port_name_t));
1c79356b 1387
3e170ce0 1388 ipc_mqueue_set_gather_member_names(space, &pset->ips_messages, maxnames, names, &actual);
1c79356b 1389
39236c6e
A
1390 /* release the portset reference */
1391 ips_release(pset);
1c79356b 1392
0a7de745 1393 if (actual <= maxnames) {
1c79356b 1394 break;
0a7de745 1395 }
1c79356b
A
1396
1397 /* didn't have enough memory; allocate more */
1c79356b 1398 kmem_free(ipc_kernel_map, addr, size);
39236c6e
A
1399 size = vm_map_round_page(
1400 (actual * sizeof(mach_port_name_t)),
0a7de745
A
1401 VM_MAP_PAGE_MASK(ipc_kernel_map)) +
1402 VM_MAP_PAGE_SIZE(ipc_kernel_map);
1c79356b
A
1403 }
1404
1405 if (actual == 0) {
1406 memory = VM_MAP_COPY_NULL;
1407
1408 kmem_free(ipc_kernel_map, addr, size);
1409 } else {
1410 vm_size_t size_used;
1411 vm_size_t vm_size_used;
1412
1413 size_used = actual * sizeof(mach_port_name_t);
39236c6e
A
1414 vm_size_used = vm_map_round_page(
1415 size_used,
1416 VM_MAP_PAGE_MASK(ipc_kernel_map));
1c79356b
A
1417
1418 /*
1419 * Make used memory pageable and get it into
1420 * copied-in form. Free any unused memory.
1421 */
1422
39236c6e
A
1423 kr = vm_map_unwire(
1424 ipc_kernel_map,
1425 vm_map_trunc_page(addr,
0a7de745 1426 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 1427 vm_map_round_page(addr + vm_size_used,
0a7de745 1428 VM_MAP_PAGE_MASK(ipc_kernel_map)),
39236c6e 1429 FALSE);
1c79356b
A
1430 assert(kr == KERN_SUCCESS);
1431
91447636 1432 kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr,
0a7de745 1433 (vm_map_size_t)size_used, TRUE, &memory);
1c79356b
A
1434 assert(kr == KERN_SUCCESS);
1435
0a7de745 1436 if (vm_size_used != size) {
1c79356b 1437 kmem_free(ipc_kernel_map,
0a7de745
A
1438 addr + vm_size_used, size - vm_size_used);
1439 }
1c79356b
A
1440 }
1441
1442 *members = (mach_port_name_t *) memory;
1443 *membersCnt = actual;
1444 return KERN_SUCCESS;
1445}
1446
f427ee49
A
1447kern_return_t
1448mach_port_get_set_status_from_user(
1449 mach_port_t port,
1450 mach_port_name_t name,
1451 mach_port_name_t **members,
1452 mach_msg_type_number_t *membersCnt)
1453{
1454 kern_return_t kr;
1455
1456 ipc_space_t space = convert_port_to_space_check_type(port, NULL, TASK_FLAVOR_READ, FALSE);
1457
1458 if (space == IPC_SPACE_NULL) {
1459 return KERN_INVALID_ARGUMENT;
1460 }
1461
1462 kr = mach_port_get_set_status(space, name, members, membersCnt);
1463
1464 ipc_space_release(space);
1465 return kr;
1466}
1467
1c79356b
A
1468/*
1469 * Routine: mach_port_move_member [kernel call]
1470 * Purpose:
1471 * If after is MACH_PORT_NULL, removes member
1472 * from the port set it is in. Otherwise, adds
1473 * member to after, removing it from any set
1474 * it might already be in.
1475 * Conditions:
1476 * Nothing locked.
1477 * Returns:
1478 * KERN_SUCCESS Moved the port.
1479 * KERN_INVALID_TASK The space is null.
1480 * KERN_INVALID_TASK The space is dead.
1481 * KERN_INVALID_NAME Member didn't denote a right.
1482 * KERN_INVALID_RIGHT Member didn't denote a receive right.
1483 * KERN_INVALID_NAME After didn't denote a right.
1484 * KERN_INVALID_RIGHT After didn't denote a port set right.
1485 * KERN_NOT_IN_SET
1486 * After is MACH_PORT_NULL and Member isn't in a port set.
1487 */
1488
1489kern_return_t
1490mach_port_move_member(
0a7de745
A
1491 ipc_space_t space,
1492 mach_port_name_t member,
1493 mach_port_name_t after)
1c79356b 1494{
cb323159 1495 ipc_object_t port_obj, ps_obj;
1c79356b 1496 ipc_port_t port;
cb323159 1497 ipc_pset_t nset = IPS_NULL;
1c79356b 1498 kern_return_t kr;
3e170ce0
A
1499 uint64_t wq_link_id = 0;
1500 uint64_t wq_reserved_prepost = 0;
1c79356b 1501
0a7de745 1502 if (space == IS_NULL) {
1c79356b 1503 return KERN_INVALID_TASK;
0a7de745 1504 }
1c79356b 1505
0a7de745 1506 if (!MACH_PORT_VALID(member)) {
1c79356b 1507 return KERN_INVALID_RIGHT;
0a7de745 1508 }
1c79356b 1509
3e170ce0 1510 if (after == MACH_PORT_DEAD) {
1c79356b 1511 return KERN_INVALID_RIGHT;
3e170ce0
A
1512 } else if (after == MACH_PORT_NULL) {
1513 wq_link_id = 0;
1514 } else {
1515 /*
1516 * We reserve both a link, and
1517 * enough prepost objects to complete
1518 * the set move atomically - we can't block
1519 * while we're holding the space lock, and
1520 * the ipc_pset_add calls ipc_mqueue_add
1521 * which may have to prepost this port onto
1522 * this set.
1523 */
1524 wq_link_id = waitq_link_reserve(NULL);
1525 wq_reserved_prepost = waitq_prepost_reserve(NULL, 10,
0a7de745 1526 WAITQ_DONT_LOCK);
d9a64523 1527 kr = ipc_pset_lazy_allocate(space, after);
0a7de745 1528 if (kr != KERN_SUCCESS) {
d9a64523 1529 goto done;
0a7de745 1530 }
3e170ce0 1531 }
1c79356b 1532
cb323159
A
1533 if (after != MACH_PORT_NULL) {
1534 kr = ipc_object_translate_two(space,
1535 member, MACH_PORT_RIGHT_RECEIVE, &port_obj,
1536 after, MACH_PORT_RIGHT_PORT_SET, &ps_obj);
1537 } else {
1538 kr = ipc_object_translate(space,
1539 member, MACH_PORT_RIGHT_RECEIVE, &port_obj);
1540 }
0a7de745 1541 if (kr != KERN_SUCCESS) {
316670eb 1542 goto done;
0a7de745 1543 }
1c79356b 1544
cb323159
A
1545 port = ip_object_to_port(port_obj);
1546 if (after != MACH_PORT_NULL) {
1547 nset = ips_object_to_pset(ps_obj);
1c79356b 1548 }
cb323159 1549 /* port and nset are locked */
1c79356b 1550
3e170ce0 1551 ipc_pset_remove_from_all(port);
1c79356b 1552
cb323159 1553 if (after != MACH_PORT_NULL) {
3e170ce0 1554 kr = ipc_pset_add(nset, port, &wq_link_id, &wq_reserved_prepost);
1c79356b
A
1555 ips_unlock(nset);
1556 }
cb323159 1557
1c79356b 1558 ip_unlock(port);
316670eb 1559
0a7de745 1560done:
3e170ce0
A
1561 /*
1562 * on success the ipc_pset_add() will consume the wq_link_id
1563 * value (resetting it to 0), so this function is always safe to call.
1564 */
1565 waitq_link_release(wq_link_id);
1566 waitq_prepost_release_reserve(wq_reserved_prepost);
316670eb 1567
1c79356b
A
1568 return kr;
1569}
1570
1571/*
1572 * Routine: mach_port_request_notification [kernel call]
1573 * Purpose:
1574 * Requests a notification. The caller supplies
1575 * a send-once right for the notification to use,
1576 * and the call returns the previously registered
1577 * send-once right, if any. Possible types:
1578 *
1579 * MACH_NOTIFY_PORT_DESTROYED
1580 * Requests a port-destroyed notification
1581 * for a receive right. Sync should be zero.
1582 * MACH_NOTIFY_NO_SENDERS
1583 * Requests a no-senders notification for a
1584 * receive right. If there are currently no
1585 * senders, sync is less than or equal to the
1586 * current make-send count, and a send-once right
1587 * is supplied, then an immediate no-senders
1588 * notification is generated.
1589 * MACH_NOTIFY_DEAD_NAME
1590 * Requests a dead-name notification for a send
1591 * or receive right. If the name is already a
1592 * dead name, sync is non-zero, and a send-once
1593 * right is supplied, then an immediate dead-name
1594 * notification is generated.
1595 * Conditions:
1596 * Nothing locked.
1597 * Returns:
1598 * KERN_SUCCESS Requested a notification.
1599 * KERN_INVALID_TASK The space is null.
1600 * KERN_INVALID_TASK The space is dead.
1601 * KERN_INVALID_VALUE Bad id value.
1602 * KERN_INVALID_NAME Name doesn't denote a right.
1603 * KERN_INVALID_RIGHT Name doesn't denote appropriate right.
1604 * KERN_INVALID_CAPABILITY The notify port is dead.
1605 * MACH_NOTIFY_PORT_DESTROYED:
1606 * KERN_INVALID_VALUE Sync isn't zero.
cb323159 1607 * KERN_FAILURE Re-registering for this notification
1c79356b
A
1608 * MACH_NOTIFY_DEAD_NAME:
1609 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1610 * KERN_INVALID_ARGUMENT Name denotes dead name, but
1611 * sync is zero or notify is IP_NULL.
1612 * KERN_UREFS_OVERFLOW Name denotes dead name, but
1613 * generating immediate notif. would overflow urefs.
1614 */
1615
1616kern_return_t
1617mach_port_request_notification(
0a7de745
A
1618 ipc_space_t space,
1619 mach_port_name_t name,
1620 mach_msg_id_t id,
1621 mach_port_mscount_t sync,
1622 ipc_port_t notify,
1623 ipc_port_t *previousp)
1c79356b
A
1624{
1625 kern_return_t kr;
1c79356b 1626
0a7de745 1627 if (space == IS_NULL) {
1c79356b 1628 return KERN_INVALID_TASK;
0a7de745 1629 }
1c79356b 1630
0a7de745 1631 if (notify == IP_DEAD) {
1c79356b 1632 return KERN_INVALID_CAPABILITY;
0a7de745 1633 }
1c79356b 1634
0a7de745 1635#if NOTYET
1c79356b
A
1636 /*
1637 * Requesting notifications on RPC ports is an error.
1638 */
91447636
A
1639 {
1640 ipc_port_t port;
0a7de745 1641 ipc_entry_t entry;
1c79356b 1642
0a7de745
A
1643 kr = ipc_right_lookup_write(space, name, &entry);
1644 if (kr != KERN_SUCCESS) {
91447636 1645 return kr;
0a7de745 1646 }
1c79356b 1647
cb323159 1648 port = ip_object_to_port(entry->ie_object);
91447636
A
1649
1650 if (port->ip_subsystem != NULL) {
1651 is_write_unlock(space);
0a7de745 1652 panic("mach_port_request_notification: on RPC port!!");
91447636
A
1653 return KERN_INVALID_CAPABILITY;
1654 }
1c79356b 1655 is_write_unlock(space);
1c79356b 1656 }
0a7de745 1657#endif /* NOTYET */
1c79356b
A
1658
1659
1660 switch (id) {
0a7de745 1661 case MACH_NOTIFY_PORT_DESTROYED: {
cb323159 1662 ipc_port_t port;
1c79356b 1663
0a7de745 1664 if (sync != 0) {
1c79356b 1665 return KERN_INVALID_VALUE;
0a7de745 1666 }
1c79356b 1667
0a7de745 1668 if (!MACH_PORT_VALID(name)) {
1c79356b 1669 return KERN_INVALID_RIGHT;
0a7de745 1670 }
1c79356b
A
1671
1672 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1673 if (kr != KERN_SUCCESS) {
1c79356b 1674 return kr;
0a7de745 1675 }
1c79356b
A
1676 /* port is locked and active */
1677
a991bd8d
A
1678 /*
1679 * you cannot register for port death notifications on a kobject,
1680 * kolabel or special reply port
1681 */
1682 if (ip_is_kobject(port) || ip_is_kolabeled(port) ||
1683 port->ip_specialreply) {
00867663
A
1684 ip_unlock(port);
1685 return KERN_INVALID_RIGHT;
1686 }
1687
cb323159
A
1688 /* Allow only one registeration of this notification */
1689 if (port->ip_pdrequest != IP_NULL) {
1690 ip_unlock(port);
1691 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_KERN_FAILURE);
1692 return KERN_FAILURE;
1693 }
1c79356b 1694
cb323159
A
1695 ipc_port_pdrequest(port, notify, previousp);
1696 /* port is unlocked */
1697 assert(*previousp == IP_NULL);
1c79356b 1698 break;
0a7de745 1699 }
1c79356b 1700
0a7de745 1701 case MACH_NOTIFY_NO_SENDERS: {
1c79356b
A
1702 ipc_port_t port;
1703
0a7de745 1704 if (!MACH_PORT_VALID(name)) {
1c79356b 1705 return KERN_INVALID_RIGHT;
0a7de745 1706 }
1c79356b
A
1707
1708 kr = ipc_port_translate_receive(space, name, &port);
0a7de745 1709 if (kr != KERN_SUCCESS) {
1c79356b 1710 return kr;
0a7de745 1711 }
1c79356b
A
1712 /* port is locked and active */
1713
1714 ipc_port_nsrequest(port, sync, notify, previousp);
1715 /* port is unlocked */
1716 break;
0a7de745 1717 }
1c79356b 1718
0a7de745 1719 case MACH_NOTIFY_SEND_POSSIBLE:
6d2010ae 1720
0a7de745
A
1721 if (!MACH_PORT_VALID(name)) {
1722 return KERN_INVALID_ARGUMENT;
6d2010ae
A
1723 }
1724
1725 kr = ipc_right_request_alloc(space, name, sync != 0,
0a7de745
A
1726 TRUE, notify, previousp);
1727 if (kr != KERN_SUCCESS) {
6d2010ae 1728 return kr;
0a7de745 1729 }
6d2010ae
A
1730 break;
1731
0a7de745 1732 case MACH_NOTIFY_DEAD_NAME:
1c79356b 1733
0a7de745 1734 if (!MACH_PORT_VALID(name)) {
1c79356b
A
1735 /*
1736 * Already dead.
1737 * Should do immediate delivery check -
1738 * will do that in the near future.
1739 */
0a7de745 1740 return KERN_INVALID_ARGUMENT;
1c79356b
A
1741 }
1742
6d2010ae 1743 kr = ipc_right_request_alloc(space, name, sync != 0,
0a7de745
A
1744 FALSE, notify, previousp);
1745 if (kr != KERN_SUCCESS) {
1c79356b 1746 return kr;
0a7de745 1747 }
1c79356b
A
1748 break;
1749
0a7de745 1750 default:
1c79356b
A
1751 return KERN_INVALID_VALUE;
1752 }
1753
1754 return KERN_SUCCESS;
1755}
1756
1757/*
1758 * Routine: mach_port_insert_right [kernel call]
1759 * Purpose:
1760 * Inserts a right into a space, as if the space
1761 * voluntarily received the right in a message,
1762 * except that the right gets the specified name.
1763 * Conditions:
1764 * Nothing locked.
1765 * Returns:
1766 * KERN_SUCCESS Inserted the right.
1767 * KERN_INVALID_TASK The space is null.
1768 * KERN_INVALID_TASK The space is dead.
1769 * KERN_INVALID_VALUE The name isn't a legal name.
1770 * KERN_NAME_EXISTS The name already denotes a right.
1771 * KERN_INVALID_VALUE Message doesn't carry a port right.
1772 * KERN_INVALID_CAPABILITY Port is null or dead.
1773 * KERN_UREFS_OVERFLOW Urefs limit would be exceeded.
1774 * KERN_RIGHT_EXISTS Space has rights under another name.
1775 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
1776 */
1777
1778kern_return_t
1779mach_port_insert_right(
0a7de745
A
1780 ipc_space_t space,
1781 mach_port_name_t name,
1782 ipc_port_t poly,
1783 mach_msg_type_name_t polyPoly)
1c79356b 1784{
0a7de745 1785 if (space == IS_NULL) {
1c79356b 1786 return KERN_INVALID_TASK;
0a7de745 1787 }
1c79356b
A
1788
1789 if (!MACH_PORT_VALID(name) ||
0a7de745 1790 !MACH_MSG_TYPE_PORT_ANY_RIGHT(polyPoly)) {
1c79356b 1791 return KERN_INVALID_VALUE;
0a7de745 1792 }
1c79356b 1793
cb323159 1794 if (!IP_VALID(poly)) {
1c79356b 1795 return KERN_INVALID_CAPABILITY;
0a7de745 1796 }
1c79356b 1797
cb323159
A
1798 return ipc_object_copyout_name(space, ip_to_object(poly),
1799 polyPoly, name);
1c79356b
A
1800}
1801
1802/*
1803 * Routine: mach_port_extract_right [kernel call]
1804 * Purpose:
1805 * Extracts a right from a space, as if the space
1806 * voluntarily sent the right to the caller.
1807 * Conditions:
1808 * Nothing locked.
1809 * Returns:
1810 * KERN_SUCCESS Extracted the right.
1811 * KERN_INVALID_TASK The space is null.
1812 * KERN_INVALID_TASK The space is dead.
1813 * KERN_INVALID_VALUE Requested type isn't a port right.
1814 * KERN_INVALID_NAME Name doesn't denote a right.
1815 * KERN_INVALID_RIGHT Name doesn't denote appropriate right.
1816 */
1817
1818kern_return_t
1819mach_port_extract_right(
0a7de745
A
1820 ipc_space_t space,
1821 mach_port_name_t name,
1822 mach_msg_type_name_t msgt_name,
1823 ipc_port_t *poly,
1824 mach_msg_type_name_t *polyPoly)
1c79356b
A
1825{
1826 kern_return_t kr;
1827
0a7de745 1828 if (space == IS_NULL) {
1c79356b 1829 return KERN_INVALID_TASK;
0a7de745 1830 }
1c79356b 1831
0a7de745 1832 if (!MACH_MSG_TYPE_PORT_ANY(msgt_name)) {
1c79356b 1833 return KERN_INVALID_VALUE;
0a7de745 1834 }
1c79356b
A
1835
1836 if (!MACH_PORT_VALID(name)) {
1837 /*
1838 * really should copy out a dead name, if it is a send or
1839 * send-once right being copied, but instead return an
1840 * error for now.
1841 */
1842 return KERN_INVALID_RIGHT;
1843 }
1844
cb323159 1845 kr = ipc_object_copyin(space, name, msgt_name, (ipc_object_t *) poly, 0, NULL,
c3c9b80d
A
1846 (space == current_space() && msgt_name == MACH_MSG_TYPE_COPY_SEND) ?
1847 IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND : IPC_OBJECT_COPYIN_FLAGS_SOFT_FAIL_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
c3c9b80d
A
2476/*
2477 * Temporary wrapper for immovable mach port guard exception.
2478 *
2479 * Condition: !(ip_is_control(port) && !immovable_control_port_enabled)
2480 */
2481void
2482mach_port_guard_exception_immovable(
2483 mach_port_name_t name,
2484 mach_port_t port,
2485 uint64_t portguard)
2486{
2487 if (ip_is_control(port) && immovable_control_port_enabled) {
2488 mach_port_guard_exception(name, 0, portguard,
2489 ipc_control_port_options & IPC_CONTROL_PORT_OPTIONS_IMMOVABLE_HARD ?
2490 kGUARD_EXC_IMMOVABLE : kGUARD_EXC_IMMOVABLE_NON_FATAL);
2491 } else if (!ip_is_control(port)) {
2492 /* always fatal exception for non-control port violation */
2493 mach_port_guard_exception(name, 0, portguard, kGUARD_EXC_IMMOVABLE);
2494 } else {
2495 /* ip_is_control(port) && !immovable_control_port_enabled */
2496 panic("mach_port_guard_exception_immovable: condition does not hold.");
2497 }
2498}
2499
39236c6e
A
2500
2501/*
2502 * Routine: mach_port_guard_ast
2503 * Purpose:
2504 * Raises an exception for mach port guard violation.
2505 * Conditions:
2506 * None.
2507 * Returns:
2508 * None.
2509 */
2510
2511void
d9a64523 2512mach_port_guard_ast(thread_t t,
0a7de745 2513 mach_exception_data_type_t code, mach_exception_data_type_t subcode)
39236c6e 2514{
d9a64523
A
2515 unsigned int reason = EXC_GUARD_DECODE_GUARD_FLAVOR(code);
2516 task_t task = t->task;
2517 unsigned int behavior = task->task_exc_guard;
2518 assert(task == current_task());
2519 assert(task != kernel_task);
5ba3f43e 2520
d9a64523 2521 switch (reason) {
0a7de745
A
2522 /*
2523 * Fatal Mach port guards - always delivered synchronously
2524 */
d9a64523
A
2525 case kGUARD_EXC_DESTROY:
2526 case kGUARD_EXC_MOD_REFS:
2527 case kGUARD_EXC_SET_CONTEXT:
2528 case kGUARD_EXC_UNGUARDED:
2529 case kGUARD_EXC_INCORRECT_GUARD:
cb323159
A
2530 case kGUARD_EXC_IMMOVABLE:
2531 case kGUARD_EXC_STRICT_REPLY:
f427ee49 2532 case kGUARD_EXC_MSG_FILTERED:
d9a64523
A
2533 task_exception_notify(EXC_GUARD, code, subcode);
2534 task_bsdtask_kill(task);
2535 break;
2536
2537 default:
2538 /*
2539 * Mach port guards controlled by task settings.
2540 */
2541
2542 /* Is delivery enabled */
2543 if ((behavior & TASK_EXC_GUARD_MP_DELIVER) == 0) {
2544 return;
2545 }
2546
2547 /* If only once, make sure we're that once */
2548 while (behavior & TASK_EXC_GUARD_MP_ONCE) {
2549 uint32_t new_behavior = behavior & ~TASK_EXC_GUARD_MP_DELIVER;
2550
2551 if (OSCompareAndSwap(behavior, new_behavior, &task->task_exc_guard)) {
2552 break;
2553 }
2554 behavior = task->task_exc_guard;
2555 if ((behavior & TASK_EXC_GUARD_MP_DELIVER) == 0) {
2556 return;
2557 }
2558 }
2559
2560 /* Raise exception via corpse fork or synchronously */
2561 if ((task->task_exc_guard & TASK_EXC_GUARD_MP_CORPSE) &&
2562 (task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) == 0) {
2563 task_violated_guard(code, subcode, NULL);
2564 } else {
2565 task_exception_notify(EXC_GUARD, code, subcode);
2566 }
39236c6e 2567
d9a64523
A
2568 /* Terminate the task if desired */
2569 if (task->task_exc_guard & TASK_EXC_GUARD_MP_FATAL) {
2570 task_bsdtask_kill(task);
2571 }
2572 break;
2573 }
39236c6e
A
2574}
2575
2576/*
2577 * Routine: mach_port_construct [kernel call]
2578 * Purpose:
2579 * Constructs a mach port with the provided set of options.
2580 * Conditions:
2581 * None.
2582 * Returns:
2583 * KERN_SUCCESS The right is allocated.
2584 * KERN_INVALID_TASK The space is null.
2585 * KERN_INVALID_TASK The space is dead.
2586 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
2587 * KERN_NO_SPACE No room in space for another right.
2588 * KERN_FAILURE Illegal option values requested.
2589 */
2590
2591kern_return_t
2592mach_port_construct(
0a7de745
A
2593 ipc_space_t space,
2594 mach_port_options_t *options,
2595 uint64_t context,
2596 mach_port_name_t *name)
39236c6e 2597{
0a7de745
A
2598 kern_return_t kr;
2599 ipc_port_t port;
94ff46dc 2600 ipc_port_init_flags_t init_flags = IPC_PORT_INIT_MESSAGE_QUEUE;
39236c6e 2601
0a7de745
A
2602 if (space == IS_NULL) {
2603 return KERN_INVALID_TASK;
2604 }
39236c6e 2605
94ff46dc
A
2606 if (options->flags & MPO_INSERT_SEND_RIGHT) {
2607 init_flags |= IPC_PORT_INIT_MAKE_SEND_RIGHT;
2608 }
2609
f427ee49
A
2610 if (options->flags & MPO_FILTER_MSG) {
2611 init_flags |= IPC_PORT_INIT_FILTER_MESSAGE;
2612 }
2613
2614 if (options->flags & MPO_TG_BLOCK_TRACKING) {
2615 /* Check the task role to allow only TASK_GRAPHICS_SERVER to set this option */
2616 if (proc_get_effective_task_policy(current_task(),
2617 TASK_POLICY_ROLE) != TASK_GRAPHICS_SERVER) {
2618 return KERN_DENIED;
2619 }
2620
2621 /*
2622 * Check the work interval port passed in to make sure it is the render server type.
2623 * Since the creation of the render server work interval is privileged, this check
2624 * acts as a guard to make sure only the render server is setting the thread group
2625 * blocking behavior on the port.
2626 */
2627 mach_port_name_t wi_port_name = options->work_interval_port;
2628 if (work_interval_port_type_render_server(wi_port_name) == false) {
2629 return KERN_INVALID_ARGUMENT;
2630 }
2631 init_flags |= IPC_PORT_INIT_TG_BLOCK_TRACKING;
2632 }
2633
39236c6e 2634 /* Allocate a new port in the IPC space */
94ff46dc 2635 kr = ipc_port_alloc(space, init_flags, name, &port);
0a7de745 2636 if (kr != KERN_SUCCESS) {
39236c6e 2637 return kr;
0a7de745
A
2638 }
2639
39236c6e
A
2640 /* Port locked and active */
2641 if (options->flags & MPO_CONTEXT_AS_GUARD) {
cb323159
A
2642 uint64_t flags = 0;
2643 if (options->flags & MPO_STRICT) {
2644 flags |= MPG_STRICT;
2645 }
2646 if (options->flags & MPO_IMMOVABLE_RECEIVE) {
2647 flags |= MPG_IMMOVABLE_RECEIVE;
2648 }
2649 kr = mach_port_guard_locked(port, (uint64_t) context, flags);
39236c6e
A
2650 /* A newly allocated and locked port should always be guarded successfully */
2651 assert(kr == KERN_SUCCESS);
2652 } else {
2653 port->ip_context = context;
2654 }
0a7de745 2655
39236c6e
A
2656 /* Unlock port */
2657 ip_unlock(port);
2658
2659 /* Set port attributes as requested */
2660
2661 if (options->flags & MPO_QLIMIT) {
2662 kr = mach_port_set_attributes(space, *name, MACH_PORT_LIMITS_INFO,
0a7de745
A
2663 (mach_port_info_t)&options->mpl, sizeof(options->mpl) / sizeof(int));
2664 if (kr != KERN_SUCCESS) {
2665 goto cleanup;
2666 }
39236c6e
A
2667 }
2668
2669 if (options->flags & MPO_TEMPOWNER) {
2670 kr = mach_port_set_attributes(space, *name, MACH_PORT_TEMPOWNER, NULL, 0);
0a7de745 2671 if (kr != KERN_SUCCESS) {
39236c6e 2672 goto cleanup;
0a7de745 2673 }
39236c6e
A
2674 }
2675
2676 if (options->flags & MPO_IMPORTANCE_RECEIVER) {
2677 kr = mach_port_set_attributes(space, *name, MACH_PORT_IMPORTANCE_RECEIVER, NULL, 0);
0a7de745 2678 if (kr != KERN_SUCCESS) {
39236c6e 2679 goto cleanup;
0a7de745 2680 }
39236c6e
A
2681 }
2682
fe8ab488
A
2683 if (options->flags & MPO_DENAP_RECEIVER) {
2684 kr = mach_port_set_attributes(space, *name, MACH_PORT_DENAP_RECEIVER, NULL, 0);
0a7de745 2685 if (kr != KERN_SUCCESS) {
fe8ab488 2686 goto cleanup;
0a7de745 2687 }
fe8ab488
A
2688 }
2689
39236c6e
A
2690 return KERN_SUCCESS;
2691
2692cleanup:
2693 /* Attempt to destroy port. If its already destroyed by some other thread, we're done */
cb323159
A
2694 (void) mach_port_destruct(space, *name,
2695 (options->flags & MPO_INSERT_SEND_RIGHT) ? -1 : 0, context);
39236c6e
A
2696 return kr;
2697}
2698
2699/*
2700 * Routine: mach_port_destruct [kernel call]
2701 * Purpose:
2702 * Destroys a mach port with appropriate guard
2703 * Conditions:
2704 * None.
2705 * Returns:
2706 * KERN_SUCCESS The name is destroyed.
2707 * KERN_INVALID_TASK The space is null.
2708 * KERN_INVALID_TASK The space is dead.
2709 * KERN_INVALID_NAME The name doesn't denote a right.
2710 * KERN_INVALID_RIGHT The right isn't correct.
2711 * KERN_INVALID_VALUE The delta for send right is incorrect.
2712 * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
2713 * This also raises a EXC_GUARD exception.
2714 */
2715
2716kern_return_t
2717mach_port_destruct(
0a7de745
A
2718 ipc_space_t space,
2719 mach_port_name_t name,
2720 mach_port_delta_t srdelta,
2721 uint64_t guard)
39236c6e 2722{
0a7de745
A
2723 kern_return_t kr;
2724 ipc_entry_t entry;
39236c6e 2725
0a7de745 2726 if (space == IS_NULL) {
39236c6e 2727 return KERN_INVALID_TASK;
0a7de745 2728 }
39236c6e 2729
0a7de745 2730 if (!MACH_PORT_VALID(name)) {
39236c6e 2731 return KERN_INVALID_NAME;
0a7de745 2732 }
39236c6e
A
2733
2734 /* Remove reference for receive right */
2735 kr = ipc_right_lookup_write(space, name, &entry);
d9a64523
A
2736 if (kr != KERN_SUCCESS) {
2737 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_NAME);
39236c6e 2738 return kr;
d9a64523 2739 }
39236c6e 2740 /* space is write-locked and active */
0a7de745 2741 kr = ipc_right_destruct(space, name, entry, srdelta, guard); /* unlocks */
39236c6e
A
2742
2743 return kr;
2744}
2745
2746/*
2747 * Routine: mach_port_guard [kernel call]
2748 * Purpose:
2749 * Guard a mach port with specified guard value.
2750 * The context field of the port is used as the guard.
2751 * Conditions:
2752 * None.
2753 * Returns:
2754 * KERN_SUCCESS The name is destroyed.
2755 * KERN_INVALID_TASK The space is null.
2756 * KERN_INVALID_TASK The space is dead.
2757 * KERN_INVALID_NAME The name doesn't denote a right.
2758 * KERN_INVALID_RIGHT The right isn't correct.
2759 * KERN_INVALID_ARGUMENT Port already contains a context/guard.
2760 */
2761kern_return_t
2762mach_port_guard(
0a7de745
A
2763 ipc_space_t space,
2764 mach_port_name_t name,
2765 uint64_t guard,
2766 boolean_t strict)
39236c6e 2767{
0a7de745
A
2768 kern_return_t kr;
2769 ipc_port_t port;
cb323159 2770 uint64_t flags = 0;
39236c6e 2771
0a7de745 2772 if (space == IS_NULL) {
39236c6e 2773 return KERN_INVALID_TASK;
0a7de745 2774 }
39236c6e 2775
0a7de745 2776 if (!MACH_PORT_VALID(name)) {
39236c6e 2777 return KERN_INVALID_NAME;
0a7de745 2778 }
39236c6e
A
2779
2780 /* Guard can be applied only to receive rights */
2781 kr = ipc_port_translate_receive(space, name, &port);
d9a64523
A
2782 if (kr != KERN_SUCCESS) {
2783 mach_port_guard_exception(name, 0, 0,
0a7de745
A
2784 ((KERN_INVALID_NAME == kr) ?
2785 kGUARD_EXC_INVALID_NAME :
2786 kGUARD_EXC_INVALID_RIGHT));
39236c6e 2787 return kr;
d9a64523 2788 }
39236c6e
A
2789
2790 /* Port locked and active */
cb323159
A
2791 if (strict) {
2792 flags = MPG_STRICT;
2793 }
2794
2795 kr = mach_port_guard_locked(port, guard, flags);
39236c6e
A
2796 ip_unlock(port);
2797
d9a64523
A
2798 if (KERN_INVALID_ARGUMENT == kr) {
2799 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_ARGUMENT);
2800 }
39236c6e 2801
d9a64523 2802 return kr;
39236c6e
A
2803}
2804
2805/*
2806 * Routine: mach_port_unguard [kernel call]
2807 * Purpose:
2808 * Unguard a mach port with specified guard value.
2809 * Conditions:
2810 * None.
2811 * Returns:
2812 * KERN_SUCCESS The name is destroyed.
2813 * KERN_INVALID_TASK The space is null.
2814 * KERN_INVALID_TASK The space is dead.
2815 * KERN_INVALID_NAME The name doesn't denote a right.
2816 * KERN_INVALID_RIGHT The right isn't correct.
2817 * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
2818 * This also raises a EXC_GUARD exception.
2819 */
2820kern_return_t
2821mach_port_unguard(
0a7de745
A
2822 ipc_space_t space,
2823 mach_port_name_t name,
2824 uint64_t guard)
39236c6e 2825{
0a7de745
A
2826 kern_return_t kr;
2827 ipc_port_t port;
39236c6e 2828
0a7de745 2829 if (space == IS_NULL) {
39236c6e 2830 return KERN_INVALID_TASK;
0a7de745 2831 }
39236c6e 2832
0a7de745 2833 if (!MACH_PORT_VALID(name)) {
39236c6e 2834 return KERN_INVALID_NAME;
0a7de745 2835 }
39236c6e
A
2836
2837 kr = ipc_port_translate_receive(space, name, &port);
d9a64523
A
2838 if (kr != KERN_SUCCESS) {
2839 mach_port_guard_exception(name, 0, 0,
0a7de745
A
2840 ((KERN_INVALID_NAME == kr) ?
2841 kGUARD_EXC_INVALID_NAME :
2842 kGUARD_EXC_INVALID_RIGHT));
39236c6e 2843 return kr;
d9a64523 2844 }
39236c6e
A
2845
2846 /* Port locked and active */
2847 kr = mach_port_unguard_locked(port, name, guard);
2848 ip_unlock(port);
d9a64523 2849
39236c6e
A
2850 return kr;
2851}
cb323159
A
2852
2853/*
2854 * Routine: mach_port_guard_with_flags [kernel call]
2855 * Purpose:
2856 * Guard a mach port with specified guard value and guard flags.
2857 * The context field of the port is used as the guard.
2858 * Conditions:
2859 * Should hold receive right for that port
2860 * Returns:
2861 * KERN_SUCCESS The name is destroyed.
2862 * KERN_INVALID_TASK The space is null.
2863 * KERN_INVALID_TASK The space is dead.
2864 * KERN_INVALID_NAME The name doesn't denote a right.
2865 * KERN_INVALID_RIGHT The right isn't correct.
2866 * KERN_INVALID_ARGUMENT Port already contains a context/guard.
2867 * KERN_INVALID_CAPABILITY Cannot set MPG_IMMOVABLE_RECEIVE flag for a port with
2868 * a movable port-destroyed notification port
2869 */
2870kern_return_t
2871mach_port_guard_with_flags(
2872 ipc_space_t space,
2873 mach_port_name_t name,
2874 uint64_t guard,
2875 uint64_t flags)
2876{
2877 kern_return_t kr;
2878 ipc_port_t port;
2879
2880 if (space == IS_NULL) {
2881 return KERN_INVALID_TASK;
2882 }
2883
2884 if (!MACH_PORT_VALID(name)) {
2885 return KERN_INVALID_NAME;
2886 }
2887
2888 kr = ipc_port_translate_receive(space, name, &port);
2889 if (kr != KERN_SUCCESS) {
2890 mach_port_guard_exception(name, 0, 0,
2891 ((KERN_INVALID_NAME == kr) ?
2892 kGUARD_EXC_INVALID_NAME :
2893 kGUARD_EXC_INVALID_RIGHT));
2894 return kr;
2895 }
2896
2897 /* Port locked and active */
2898 kr = mach_port_guard_locked(port, guard, flags);
2899 ip_unlock(port);
2900
2901 if (KERN_INVALID_ARGUMENT == kr) {
2902 mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_ARGUMENT);
2903 }
2904
2905 return kr;
2906}
2907
2908/*
2909 * Routine: mach_port_swap_guard [kernel call]
2910 * Purpose:
2911 * Swap guard value.
2912 * Conditions:
2913 * Port should already be guarded.
2914 * Returns:
2915 * KERN_SUCCESS The name is destroyed.
2916 * KERN_INVALID_TASK The space is null.
2917 * KERN_INVALID_TASK The space is dead.
2918 * KERN_INVALID_NAME The name doesn't denote a right.
2919 * KERN_INVALID_RIGHT The right isn't correct.
2920 * KERN_INVALID_ARGUMENT Port doesn't contain a guard; is strictly guarded
2921 * or the old_guard doesnt match the context
2922 */
2923kern_return_t
2924mach_port_swap_guard(
2925 ipc_space_t space,
2926 mach_port_name_t name,
2927 uint64_t old_guard,
2928 uint64_t new_guard)
2929{
2930 kern_return_t kr;
2931 ipc_port_t port;
2932
2933 if (space == IS_NULL) {
2934 return KERN_INVALID_TASK;
2935 }
2936
2937 if (!MACH_PORT_VALID(name)) {
2938 return KERN_INVALID_NAME;
2939 }
2940
2941 kr = ipc_port_translate_receive(space, name, &port);
2942 if (kr != KERN_SUCCESS) {
2943 mach_port_guard_exception(name, 0, 0,
2944 ((KERN_INVALID_NAME == kr) ?
2945 kGUARD_EXC_INVALID_NAME :
2946 kGUARD_EXC_INVALID_RIGHT));
2947 return kr;
2948 }
2949
2950 /* Port locked and active */
2951 if (!port->ip_guarded) {
2952 ip_unlock(port);
2953 mach_port_guard_exception(name, old_guard, 0, kGUARD_EXC_UNGUARDED);
2954 return KERN_INVALID_ARGUMENT;
2955 }
2956
2957 if (port->ip_strict_guard) {
2958 uint64_t portguard = port->ip_context;
2959 ip_unlock(port);
2960 /* For strictly guarded ports, disallow overwriting context; Raise Exception */
2961 mach_port_guard_exception(name, old_guard, portguard, kGUARD_EXC_SET_CONTEXT);
2962 return KERN_INVALID_ARGUMENT;
2963 }
2964
2965 if (port->ip_context != old_guard) {
2966 uint64_t portguard = port->ip_context;
2967 ip_unlock(port);
2968 mach_port_guard_exception(name, old_guard, portguard, kGUARD_EXC_INCORRECT_GUARD);
2969 return KERN_INVALID_ARGUMENT;
2970 }
2971
2972 imq_lock(&port->ip_messages);
2973 port->ip_context = new_guard;
2974 imq_unlock(&port->ip_messages);
2975
2976 ip_unlock(port);
2977
2978 return KERN_SUCCESS;
2979}