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