]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/ipc_tt.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / kern / ipc_tt.c
CommitLineData
1c79356b 1/*
316670eb 2 * Copyright (c) 2000-2010 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,1988,1987 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 */
1c79356b
A
62/*
63 */
64
65/*
66 * File: ipc_tt.c
67 * Purpose:
68 * Task and thread related IPC functions.
69 */
70
55e303ae 71#include <mach/mach_types.h>
1c79356b 72#include <mach/boolean.h>
1c79356b
A
73#include <mach/kern_return.h>
74#include <mach/mach_param.h>
75#include <mach/task_special_ports.h>
76#include <mach/thread_special_ports.h>
77#include <mach/thread_status.h>
78#include <mach/exception_types.h>
91447636 79#include <mach/memory_object_types.h>
1c79356b
A
80#include <mach/mach_traps.h>
81#include <mach/task_server.h>
82#include <mach/thread_act_server.h>
83#include <mach/mach_host_server.h>
91447636 84#include <mach/host_priv_server.h>
1c79356b 85#include <mach/vm_map_server.h>
91447636
A
86
87#include <kern/kern_types.h>
1c79356b 88#include <kern/host.h>
91447636 89#include <kern/ipc_kobject.h>
1c79356b 90#include <kern/ipc_tt.h>
91447636
A
91#include <kern/kalloc.h>
92#include <kern/thread.h>
1c79356b 93#include <kern/misc_protos.h>
c3c9b80d 94#include <kdp/kdp_dyld.h>
91447636
A
95
96#include <vm/vm_map.h>
1c79356b 97#include <vm/vm_pageout.h>
91447636
A
98#include <vm/vm_protos.h>
99
2d21ac55
A
100#include <security/mac_mach_internal.h>
101
4ba76501
A
102#if CONFIG_CSR
103#include <sys/csr.h>
104#endif
105
f427ee49 106#if !defined(XNU_TARGET_OS_OSX) && !SECURE_KERNEL
5ba3f43e
A
107extern int cs_relax_platform_task_ports;
108#endif
109
f427ee49
A
110extern boolean_t IOTaskHasEntitlement(task_t, const char *);
111
91447636 112/* forward declarations */
f427ee49
A
113static kern_return_t port_allowed_with_task_flavor(int which, mach_task_flavor_t flavor);
114static kern_return_t port_allowed_with_thread_flavor(int which, mach_thread_flavor_t flavor);
5ba3f43e
A
115static void ipc_port_bind_special_reply_port_locked(ipc_port_t port);
116static kern_return_t ipc_port_unbind_special_reply_port(thread_t thread, boolean_t unbind_active_port);
117kern_return_t task_conversion_eval(task_t caller, task_t victim);
f427ee49 118static ipc_space_t convert_port_to_space_no_eval(ipc_port_t port);
f427ee49
A
119static thread_t convert_port_to_thread_no_eval(ipc_port_t port);
120static ipc_port_t convert_task_to_port_with_flavor(task_t task, mach_task_flavor_t flavor);
121static ipc_port_t convert_thread_to_port_with_flavor(thread_t thread, mach_thread_flavor_t flavor);
c3c9b80d
A
122static task_read_t convert_port_to_task_read_no_eval(ipc_port_t port);
123static thread_read_t convert_port_to_thread_read_no_eval(ipc_port_t port);
124static ipc_space_read_t convert_port_to_space_read_no_eval(ipc_port_t port);
1c79356b
A
125
126/*
127 * Routine: ipc_task_init
128 * Purpose:
129 * Initialize a task's IPC state.
130 *
131 * If non-null, some state will be inherited from the parent.
132 * The parent must be appropriately initialized.
133 * Conditions:
134 * Nothing locked.
135 */
136
137void
138ipc_task_init(
0a7de745
A
139 task_t task,
140 task_t parent)
1c79356b
A
141{
142 ipc_space_t space;
143 ipc_port_t kport;
0c530ab8 144 ipc_port_t nport;
c3c9b80d 145 ipc_port_t pport;
1c79356b
A
146 kern_return_t kr;
147 int i;
148
149
ea3f0419 150 kr = ipc_space_create(&ipc_table_entries[0], IPC_LABEL_NONE, &space);
0a7de745 151 if (kr != KERN_SUCCESS) {
1c79356b 152 panic("ipc_task_init");
0a7de745 153 }
1c79356b 154
2d21ac55 155 space->is_task = task;
1c79356b 156
c3c9b80d
A
157 if (immovable_control_port_enabled) {
158 ipc_kobject_alloc_options_t options = IPC_KOBJECT_ALLOC_IMMOVABLE_SEND;
159 if (pinned_control_port_enabled) {
160 options |= IPC_KOBJECT_ALLOC_PINNED;
161 }
162 pport = ipc_kobject_alloc_port(IKO_NULL, IKOT_NONE, options);
f427ee49 163
c3c9b80d
A
164 kport = ipc_kobject_alloc_labeled_port(IKO_NULL, IKOT_TASK_CONTROL,
165 IPC_LABEL_SUBST_TASK, IPC_KOBJECT_ALLOC_NONE);
166 kport->ip_alt_port = pport;
167 } else {
168 kport = ipc_kobject_alloc_port(IKO_NULL, IKOT_TASK_CONTROL,
169 IPC_KOBJECT_ALLOC_NONE);
170
171 pport = kport;
0a7de745 172 }
1c79356b 173
0c530ab8 174 nport = ipc_port_alloc_kernel();
0a7de745 175 if (nport == IP_NULL) {
0c530ab8 176 panic("ipc_task_init");
0a7de745 177 }
0c530ab8 178
c3c9b80d
A
179 if (pport == IP_NULL) {
180 panic("ipc_task_init");
181 }
182
1c79356b 183 itk_lock_init(task);
c3c9b80d
A
184 task->itk_task_ports[TASK_FLAVOR_CONTROL] = kport;
185 task->itk_task_ports[TASK_FLAVOR_NAME] = nport;
f427ee49
A
186
187 /* Lazily allocated on-demand */
c3c9b80d
A
188 task->itk_task_ports[TASK_FLAVOR_INSPECT] = IP_NULL;
189 task->itk_task_ports[TASK_FLAVOR_READ] = IP_NULL;
190 task->itk_dyld_notify = NULL;
f427ee49 191
c3c9b80d
A
192 task->itk_self = pport;
193 task->itk_resume = IP_NULL; /* Lazily allocated on-demand */
39037602
A
194 if (task_is_a_corpse_fork(task)) {
195 /*
196 * No sender's notification for corpse would not
197 * work with a naked send right in kernel.
198 */
f427ee49 199 task->itk_settable_self = IP_NULL;
39037602 200 } else {
f427ee49 201 task->itk_settable_self = ipc_port_make_send(kport);
39037602 202 }
fe8ab488 203 task->itk_debug_control = IP_NULL;
1c79356b 204 task->itk_space = space;
1c79356b 205
5ba3f43e 206#if CONFIG_MACF
b226f5e5 207 task->exc_actions[0].label = NULL;
5ba3f43e
A
208 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
209 mac_exc_associate_action_label(&task->exc_actions[i], mac_exc_create_label());
210 }
211#endif
b226f5e5
A
212
213 /* always zero-out the first (unused) array element */
b226f5e5 214 bzero(&task->exc_actions[0], sizeof(task->exc_actions[0]));
0a7de745 215
1c79356b 216 if (parent == TASK_NULL) {
55e303ae 217 ipc_port_t port;
1c79356b
A
218 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
219 task->exc_actions[i].port = IP_NULL;
b226f5e5
A
220 task->exc_actions[i].flavor = 0;
221 task->exc_actions[i].behavior = 0;
222 task->exc_actions[i].privileged = FALSE;
1c79356b 223 }/* for */
0a7de745 224
55e303ae
A
225 kr = host_get_host_port(host_priv_self(), &port);
226 assert(kr == KERN_SUCCESS);
227 task->itk_host = port;
228
1c79356b 229 task->itk_bootstrap = IP_NULL;
2d21ac55
A
230 task->itk_seatbelt = IP_NULL;
231 task->itk_gssd = IP_NULL;
2d21ac55 232 task->itk_task_access = IP_NULL;
55e303ae 233
0a7de745 234 for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
1c79356b 235 task->itk_registered[i] = IP_NULL;
0a7de745 236 }
1c79356b
A
237 } else {
238 itk_lock(parent);
c3c9b80d 239 assert(parent->itk_task_ports[TASK_FLAVOR_CONTROL] != IP_NULL);
1c79356b
A
240
241 /* inherit registered ports */
242
0a7de745 243 for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
1c79356b 244 task->itk_registered[i] =
0a7de745
A
245 ipc_port_copy_send(parent->itk_registered[i]);
246 }
1c79356b
A
247
248 /* inherit exception and bootstrap ports */
249
250 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
0a7de745
A
251 task->exc_actions[i].port =
252 ipc_port_copy_send(parent->exc_actions[i].port);
253 task->exc_actions[i].flavor =
254 parent->exc_actions[i].flavor;
255 task->exc_actions[i].behavior =
256 parent->exc_actions[i].behavior;
257 task->exc_actions[i].privileged =
258 parent->exc_actions[i].privileged;
39037602 259#if CONFIG_MACF
0a7de745 260 mac_exc_inherit_action_label(parent->exc_actions + i, task->exc_actions + i);
39037602 261#endif
1c79356b
A
262 }/* for */
263 task->itk_host =
0a7de745 264 ipc_port_copy_send(parent->itk_host);
1c79356b
A
265
266 task->itk_bootstrap =
0a7de745 267 ipc_port_copy_send(parent->itk_bootstrap);
1c79356b 268
2d21ac55 269 task->itk_seatbelt =
0a7de745 270 ipc_port_copy_send(parent->itk_seatbelt);
2d21ac55
A
271
272 task->itk_gssd =
0a7de745 273 ipc_port_copy_send(parent->itk_gssd);
2d21ac55 274
2d21ac55 275 task->itk_task_access =
0a7de745 276 ipc_port_copy_send(parent->itk_task_access);
2d21ac55 277
1c79356b
A
278 itk_unlock(parent);
279 }
280}
281
282/*
283 * Routine: ipc_task_enable
284 * Purpose:
285 * Enable a task for IPC access.
286 * Conditions:
287 * Nothing locked.
288 */
289
290void
291ipc_task_enable(
0a7de745 292 task_t task)
1c79356b
A
293{
294 ipc_port_t kport;
0c530ab8 295 ipc_port_t nport;
f427ee49
A
296 ipc_port_t iport;
297 ipc_port_t rdport;
c3c9b80d 298 ipc_port_t pport;
1c79356b
A
299
300 itk_lock(task);
c3c9b80d
A
301
302 assert(!task->ipc_active || task_is_a_corpse(task));
303 task->ipc_active = true;
304
305 kport = task->itk_task_ports[TASK_FLAVOR_CONTROL];
0a7de745 306 if (kport != IP_NULL) {
f427ee49 307 ipc_kobject_set(kport, (ipc_kobject_t) task, IKOT_TASK_CONTROL);
0a7de745 308 }
c3c9b80d 309 nport = task->itk_task_ports[TASK_FLAVOR_NAME];
0a7de745 310 if (nport != IP_NULL) {
0c530ab8 311 ipc_kobject_set(nport, (ipc_kobject_t) task, IKOT_TASK_NAME);
0a7de745 312 }
c3c9b80d 313 iport = task->itk_task_ports[TASK_FLAVOR_INSPECT];
f427ee49
A
314 if (iport != IP_NULL) {
315 ipc_kobject_set(iport, (ipc_kobject_t) task, IKOT_TASK_INSPECT);
316 }
c3c9b80d 317 rdport = task->itk_task_ports[TASK_FLAVOR_READ];
f427ee49
A
318 if (rdport != IP_NULL) {
319 ipc_kobject_set(rdport, (ipc_kobject_t) task, IKOT_TASK_READ);
320 }
c3c9b80d
A
321 pport = task->itk_self;
322 if (immovable_control_port_enabled && pport != IP_NULL) {
323 ipc_kobject_set(pport, (ipc_kobject_t) task, IKOT_TASK_CONTROL);
324 }
f427ee49 325
1c79356b
A
326 itk_unlock(task);
327}
328
329/*
330 * Routine: ipc_task_disable
331 * Purpose:
332 * Disable IPC access to a task.
333 * Conditions:
334 * Nothing locked.
335 */
336
337void
338ipc_task_disable(
0a7de745 339 task_t task)
1c79356b
A
340{
341 ipc_port_t kport;
0c530ab8 342 ipc_port_t nport;
f427ee49
A
343 ipc_port_t iport;
344 ipc_port_t rdport;
39236c6e 345 ipc_port_t rport;
c3c9b80d 346 ipc_port_t pport;
1c79356b
A
347
348 itk_lock(task);
c3c9b80d
A
349
350 /*
351 * This innocuous looking line is load bearing.
352 *
353 * It is used to disable the creation of lazy made ports.
354 * We must do so before we drop the last reference on the task,
355 * as task ports do not own a reference on the task, and
356 * convert_port_to_task* will crash trying to resurect a task.
357 */
358 task->ipc_active = false;
359
360 kport = task->itk_task_ports[TASK_FLAVOR_CONTROL];
0a7de745 361 if (kport != IP_NULL) {
c3c9b80d
A
362 ip_lock(kport);
363 kport->ip_alt_port = IP_NULL;
364 ipc_kobject_set_atomically(kport, IKO_NULL, IKOT_NONE);
365 ip_unlock(kport);
0a7de745 366 }
c3c9b80d 367 nport = task->itk_task_ports[TASK_FLAVOR_NAME];
0a7de745 368 if (nport != IP_NULL) {
0c530ab8 369 ipc_kobject_set(nport, IKO_NULL, IKOT_NONE);
0a7de745 370 }
c3c9b80d 371 iport = task->itk_task_ports[TASK_FLAVOR_INSPECT];
f427ee49
A
372 if (iport != IP_NULL) {
373 ipc_kobject_set(iport, IKO_NULL, IKOT_NONE);
374 }
c3c9b80d 375 rdport = task->itk_task_ports[TASK_FLAVOR_READ];
f427ee49
A
376 if (rdport != IP_NULL) {
377 ipc_kobject_set(rdport, IKO_NULL, IKOT_NONE);
378 }
c3c9b80d
A
379 pport = task->itk_self;
380 if (pport != kport && pport != IP_NULL) {
381 assert(immovable_control_port_enabled);
382 assert(pport->ip_immovable_send);
383 ipc_kobject_set(pport, IKO_NULL, IKOT_NONE);
384 }
39236c6e
A
385
386 rport = task->itk_resume;
387 if (rport != IP_NULL) {
388 /*
389 * From this point onwards this task is no longer accepting
390 * resumptions.
391 *
392 * There are still outstanding suspensions on this task,
393 * even as it is being torn down. Disconnect the task
0a7de745 394 * from the rport, thereby "orphaning" the rport. The rport
39236c6e
A
395 * itself will go away only when the last suspension holder
396 * destroys his SO right to it -- when he either
397 * exits, or tries to actually use that last SO right to
398 * resume this (now non-existent) task.
399 */
400 ipc_kobject_set(rport, IKO_NULL, IKOT_NONE);
401 }
1c79356b
A
402 itk_unlock(task);
403}
404
405/*
406 * Routine: ipc_task_terminate
407 * Purpose:
408 * Clean up and destroy a task's IPC state.
409 * Conditions:
410 * Nothing locked. The task must be suspended.
411 * (Or the current thread must be in the task.)
412 */
413
414void
415ipc_task_terminate(
0a7de745 416 task_t task)
1c79356b
A
417{
418 ipc_port_t kport;
0c530ab8 419 ipc_port_t nport;
f427ee49
A
420 ipc_port_t iport;
421 ipc_port_t rdport;
0a7de745 422 ipc_port_t rport;
c3c9b80d
A
423 ipc_port_t pport;
424 ipc_port_t sself;
425 ipc_port_t *notifiers_ptr = NULL;
1c79356b
A
426
427 itk_lock(task);
c3c9b80d
A
428
429 /*
430 * If we ever failed to clear ipc_active before the last reference
431 * was dropped, lazy ports might be made and used after the last
432 * reference is dropped and cause use after free (see comment in
433 * ipc_task_disable()).
434 */
435 assert(!task->ipc_active);
436
437 kport = task->itk_task_ports[TASK_FLAVOR_CONTROL];
438 sself = task->itk_settable_self;
1c79356b
A
439
440 if (kport == IP_NULL) {
441 /* the task is already terminated (can this happen?) */
442 itk_unlock(task);
443 return;
444 }
c3c9b80d 445 task->itk_task_ports[TASK_FLAVOR_CONTROL] = IP_NULL;
f427ee49 446
c3c9b80d
A
447 rdport = task->itk_task_ports[TASK_FLAVOR_READ];
448 task->itk_task_ports[TASK_FLAVOR_READ] = IP_NULL;
0c530ab8 449
c3c9b80d
A
450 iport = task->itk_task_ports[TASK_FLAVOR_INSPECT];
451 task->itk_task_ports[TASK_FLAVOR_INSPECT] = IP_NULL;
f427ee49 452
c3c9b80d 453 nport = task->itk_task_ports[TASK_FLAVOR_NAME];
0c530ab8 454 assert(nport != IP_NULL);
c3c9b80d
A
455 task->itk_task_ports[TASK_FLAVOR_NAME] = IP_NULL;
456
457 if (task->itk_dyld_notify) {
458 notifiers_ptr = task->itk_dyld_notify;
459 task->itk_dyld_notify = NULL;
460 }
461
462 if (immovable_control_port_enabled) {
463 pport = task->itk_self;
464 assert(pport != IP_NULL);
465 }
466
467 task->itk_self = IP_NULL;
0c530ab8 468
39236c6e
A
469 rport = task->itk_resume;
470 task->itk_resume = IP_NULL;
471
1c79356b
A
472 itk_unlock(task);
473
474 /* release the naked send rights */
c3c9b80d
A
475 if (IP_VALID(sself)) {
476 ipc_port_release_send(sself);
477 }
1c79356b 478
c3c9b80d
A
479 if (notifiers_ptr) {
480 for (int i = 0; i < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; i++) {
481 if (IP_VALID(notifiers_ptr[i])) {
482 ipc_port_release_send(notifiers_ptr[i]);
483 }
484 }
485 kfree(notifiers_ptr, DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT * sizeof(ipc_port_t));
0a7de745 486 }
1c79356b 487
c3c9b80d 488 for (int i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1c79356b
A
489 if (IP_VALID(task->exc_actions[i].port)) {
490 ipc_port_release_send(task->exc_actions[i].port);
491 }
39037602 492#if CONFIG_MACF
5ba3f43e 493 mac_exc_free_action_label(task->exc_actions + i);
39037602 494#endif
91447636
A
495 }
496
0a7de745 497 if (IP_VALID(task->itk_host)) {
1c79356b 498 ipc_port_release_send(task->itk_host);
0a7de745 499 }
1c79356b 500
0a7de745 501 if (IP_VALID(task->itk_bootstrap)) {
1c79356b 502 ipc_port_release_send(task->itk_bootstrap);
0a7de745 503 }
1c79356b 504
0a7de745 505 if (IP_VALID(task->itk_seatbelt)) {
2d21ac55 506 ipc_port_release_send(task->itk_seatbelt);
0a7de745
A
507 }
508
509 if (IP_VALID(task->itk_gssd)) {
2d21ac55 510 ipc_port_release_send(task->itk_gssd);
0a7de745 511 }
2d21ac55 512
0a7de745 513 if (IP_VALID(task->itk_task_access)) {
2d21ac55 514 ipc_port_release_send(task->itk_task_access);
0a7de745 515 }
2d21ac55 516
0a7de745 517 if (IP_VALID(task->itk_debug_control)) {
fe8ab488 518 ipc_port_release_send(task->itk_debug_control);
0a7de745 519 }
fe8ab488 520
c3c9b80d 521 for (int i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
0a7de745 522 if (IP_VALID(task->itk_registered[i])) {
1c79356b 523 ipc_port_release_send(task->itk_registered[i]);
0a7de745
A
524 }
525 }
1c79356b 526
0c530ab8 527 /* destroy the kernel ports */
c3c9b80d
A
528 if (immovable_control_port_enabled) {
529 ip_lock(kport);
530 kport->ip_alt_port = IP_NULL;
531 ipc_kobject_set_atomically(kport, IKO_NULL, IKOT_NONE);
532 ip_unlock(kport);
533
534 /* pport == kport if immovability is off */
535 ipc_port_dealloc_kernel(pport);
536 }
1c79356b 537 ipc_port_dealloc_kernel(kport);
0c530ab8 538 ipc_port_dealloc_kernel(nport);
f427ee49
A
539 if (iport != IP_NULL) {
540 ipc_port_dealloc_kernel(iport);
541 }
542 if (rdport != IP_NULL) {
543 ipc_port_dealloc_kernel(rdport);
544 }
0a7de745
A
545 if (rport != IP_NULL) {
546 ipc_port_dealloc_kernel(rport);
547 }
b0d623f7
A
548
549 itk_lock_destroy(task);
1c79356b
A
550}
551
55e303ae
A
552/*
553 * Routine: ipc_task_reset
554 * Purpose:
555 * Reset a task's IPC state to protect it when
0c530ab8 556 * it enters an elevated security context. The
f427ee49
A
557 * task name port can remain the same - since it
558 * represents no specific privilege.
55e303ae
A
559 * Conditions:
560 * Nothing locked. The task must be suspended.
561 * (Or the current thread must be in the task.)
562 */
563
564void
565ipc_task_reset(
0a7de745 566 task_t task)
55e303ae 567{
c3c9b80d 568 ipc_port_t old_kport, old_pport, new_kport, new_pport;
55e303ae 569 ipc_port_t old_sself;
f427ee49
A
570 ipc_port_t old_rdport;
571 ipc_port_t old_iport;
55e303ae 572 ipc_port_t old_exc_actions[EXC_TYPES_COUNT];
c3c9b80d 573 ipc_port_t *notifiers_ptr = NULL;
55e303ae 574
5ba3f43e
A
575#if CONFIG_MACF
576 /* Fresh label to unset credentials in existing labels. */
577 struct label *unset_label = mac_exc_create_label();
578#endif
0a7de745 579
c3c9b80d
A
580 if (immovable_control_port_enabled) {
581 ipc_kobject_alloc_options_t options = IPC_KOBJECT_ALLOC_IMMOVABLE_SEND;
582 if (pinned_control_port_enabled) {
583 options |= IPC_KOBJECT_ALLOC_PINNED;
584 }
585
586 new_pport = ipc_kobject_alloc_port((ipc_kobject_t)task,
587 IKOT_TASK_CONTROL, options);
588
589 new_kport = ipc_kobject_alloc_labeled_port((ipc_kobject_t)task,
590 IKOT_TASK_CONTROL, IPC_LABEL_SUBST_TASK,
591 IPC_KOBJECT_ALLOC_NONE);
592 new_kport->ip_alt_port = new_pport;
593 } else {
594 new_kport = ipc_kobject_alloc_port((ipc_kobject_t)task,
595 IKOT_TASK_CONTROL, IPC_KOBJECT_ALLOC_NONE);
596
597 new_pport = new_kport;
598 }
55e303ae
A
599
600 itk_lock(task);
601
c3c9b80d
A
602 old_kport = task->itk_task_ports[TASK_FLAVOR_CONTROL];
603 old_rdport = task->itk_task_ports[TASK_FLAVOR_READ];
604 old_iport = task->itk_task_ports[TASK_FLAVOR_INSPECT];
55e303ae 605
c3c9b80d
A
606 old_pport = task->itk_self;
607
608 if (old_pport == IP_NULL) {
55e303ae
A
609 /* the task is already terminated (can this happen?) */
610 itk_unlock(task);
611 ipc_port_dealloc_kernel(new_kport);
c3c9b80d
A
612 if (immovable_control_port_enabled) {
613 ipc_port_dealloc_kernel(new_pport);
614 }
5ba3f43e
A
615#if CONFIG_MACF
616 mac_exc_free_label(unset_label);
617#endif
55e303ae
A
618 return;
619 }
620
f427ee49 621 old_sself = task->itk_settable_self;
c3c9b80d
A
622 task->itk_task_ports[TASK_FLAVOR_CONTROL] = new_kport;
623 task->itk_self = new_pport;
624
625 task->itk_settable_self = ipc_port_make_send(new_kport);
39037602
A
626
627 /* Set the old kport to IKOT_NONE and update the exec token while under the port lock */
628 ip_lock(old_kport);
c3c9b80d 629 old_kport->ip_alt_port = IP_NULL;
39037602
A
630 ipc_kobject_set_atomically(old_kport, IKO_NULL, IKOT_NONE);
631 task->exec_token += 1;
632 ip_unlock(old_kport);
633
f427ee49 634 /* Reset the read and inspect flavors of task port */
c3c9b80d
A
635 task->itk_task_ports[TASK_FLAVOR_READ] = IP_NULL;
636 task->itk_task_ports[TASK_FLAVOR_INSPECT] = IP_NULL;
f427ee49 637
c3c9b80d
A
638 if (immovable_control_port_enabled) {
639 ip_lock(old_pport);
640 ipc_kobject_set_atomically(old_pport, IKO_NULL, IKOT_NONE);
641 task->exec_token += 1;
642 ip_unlock(old_pport);
643 }
644
645 for (int i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
3e170ce0
A
646 old_exc_actions[i] = IP_NULL;
647
648 if (i == EXC_CORPSE_NOTIFY && task_corpse_pending_report(task)) {
649 continue;
650 }
651
8ad349bb 652 if (!task->exc_actions[i].privileged) {
39037602 653#if CONFIG_MACF
5ba3f43e 654 mac_exc_update_action_label(task->exc_actions + i, unset_label);
39037602 655#endif
8ad349bb
A
656 old_exc_actions[i] = task->exc_actions[i].port;
657 task->exc_actions[i].port = IP_NULL;
8ad349bb 658 }
55e303ae 659 }/* for */
0a7de745 660
fe8ab488
A
661 if (IP_VALID(task->itk_debug_control)) {
662 ipc_port_release_send(task->itk_debug_control);
663 }
664 task->itk_debug_control = IP_NULL;
0a7de745 665
c3c9b80d
A
666 if (task->itk_dyld_notify) {
667 notifiers_ptr = task->itk_dyld_notify;
668 task->itk_dyld_notify = NULL;
669 }
670
55e303ae
A
671 itk_unlock(task);
672
5ba3f43e
A
673#if CONFIG_MACF
674 mac_exc_free_label(unset_label);
675#endif
676
55e303ae
A
677 /* release the naked send rights */
678
0a7de745 679 if (IP_VALID(old_sself)) {
55e303ae 680 ipc_port_release_send(old_sself);
0a7de745 681 }
55e303ae 682
c3c9b80d
A
683 if (notifiers_ptr) {
684 for (int i = 0; i < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; i++) {
685 if (IP_VALID(notifiers_ptr[i])) {
686 ipc_port_release_send(notifiers_ptr[i]);
687 }
688 }
689 kfree(notifiers_ptr, DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT * sizeof(ipc_port_t));
690 }
691
692 for (int i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
55e303ae
A
693 if (IP_VALID(old_exc_actions[i])) {
694 ipc_port_release_send(old_exc_actions[i]);
695 }
696 }/* for */
55e303ae 697
f427ee49 698 /* destroy all task port flavors */
55e303ae 699 ipc_port_dealloc_kernel(old_kport);
c3c9b80d
A
700 if (immovable_control_port_enabled) {
701 ipc_port_dealloc_kernel(old_pport);
702 }
f427ee49
A
703 if (old_rdport != IP_NULL) {
704 ipc_port_dealloc_kernel(old_rdport);
705 }
706 if (old_iport != IP_NULL) {
707 ipc_port_dealloc_kernel(old_iport);
708 }
55e303ae
A
709}
710
1c79356b
A
711/*
712 * Routine: ipc_thread_init
713 * Purpose:
714 * Initialize a thread's IPC state.
715 * Conditions:
716 * Nothing locked.
717 */
718
719void
720ipc_thread_init(
c3c9b80d
A
721 thread_t thread,
722 ipc_thread_init_options_t options)
1c79356b 723{
0a7de745 724 ipc_port_t kport;
c3c9b80d
A
725 ipc_port_t pport;
726 ipc_kobject_alloc_options_t alloc_options = IPC_KOBJECT_ALLOC_NONE;
727
728 /*
729 * Having immovable_control_port_enabled boot-arg set does not guarantee
730 * thread control port should be made immovable/pinned, also check options.
731 *
732 * raw mach threads created via thread_create() have neither of INIT_PINNED
733 * or INIT_IMMOVABLE set.
734 */
735 if (immovable_control_port_enabled && (options & IPC_THREAD_INIT_IMMOVABLE)) {
736 alloc_options |= IPC_KOBJECT_ALLOC_IMMOVABLE_SEND;
737
738 if (pinned_control_port_enabled && (options & IPC_THREAD_INIT_PINNED)) {
739 alloc_options |= IPC_KOBJECT_ALLOC_PINNED;
740 }
741
742 pport = ipc_kobject_alloc_port((ipc_kobject_t)thread,
743 IKOT_THREAD_CONTROL, alloc_options);
744
745 kport = ipc_kobject_alloc_labeled_port((ipc_kobject_t)thread,
746 IKOT_THREAD_CONTROL, IPC_LABEL_SUBST_THREAD, IPC_KOBJECT_ALLOC_NONE);
747 kport->ip_alt_port = pport;
748 } else {
749 kport = ipc_kobject_alloc_port((ipc_kobject_t)thread,
750 IKOT_THREAD_CONTROL, IPC_KOBJECT_ALLOC_NONE);
751
752 pport = kport;
753 }
754
755 thread->ith_thread_ports[THREAD_FLAVOR_CONTROL] = kport;
756
757 thread->ith_settable_self = ipc_port_make_send(kport);
91447636 758
c3c9b80d 759 thread->ith_self = pport;
91447636 760
5ba3f43e 761 thread->ith_special_reply_port = NULL;
39236c6e 762 thread->exc_actions = NULL;
91447636 763
39236c6e
A
764#if IMPORTANCE_INHERITANCE
765 thread->ith_assertions = 0;
766#endif
767
c3c9b80d 768 thread->ipc_active = true;
1c79356b 769 ipc_kmsg_queue_init(&thread->ith_messages);
91447636 770
1c79356b
A
771 thread->ith_rpc_reply = IP_NULL;
772}
773
39236c6e
A
774void
775ipc_thread_init_exc_actions(
0a7de745 776 thread_t thread)
39236c6e
A
777{
778 assert(thread->exc_actions == NULL);
779
780 thread->exc_actions = kalloc(sizeof(struct exception_action) * EXC_TYPES_COUNT);
781 bzero(thread->exc_actions, sizeof(struct exception_action) * EXC_TYPES_COUNT);
39037602
A
782
783#if CONFIG_MACF
784 for (size_t i = 0; i < EXC_TYPES_COUNT; ++i) {
5ba3f43e 785 mac_exc_associate_action_label(thread->exc_actions + i, mac_exc_create_label());
39037602
A
786 }
787#endif
39236c6e
A
788}
789
790void
791ipc_thread_destroy_exc_actions(
0a7de745 792 thread_t thread)
39236c6e
A
793{
794 if (thread->exc_actions != NULL) {
39037602
A
795#if CONFIG_MACF
796 for (size_t i = 0; i < EXC_TYPES_COUNT; ++i) {
5ba3f43e 797 mac_exc_free_action_label(thread->exc_actions + i);
39037602
A
798 }
799#endif
800
0a7de745
A
801 kfree(thread->exc_actions,
802 sizeof(struct exception_action) * EXC_TYPES_COUNT);
39236c6e
A
803 thread->exc_actions = NULL;
804 }
805}
806
f427ee49
A
807/*
808 * Routine: ipc_thread_disable
809 * Purpose:
810 * Clean up and destroy a thread's IPC state.
811 * Conditions:
812 * Thread locked.
813 */
1c79356b 814void
91447636 815ipc_thread_disable(
0a7de745 816 thread_t thread)
1c79356b 817{
c3c9b80d
A
818 ipc_port_t kport = thread->ith_thread_ports[THREAD_FLAVOR_CONTROL];
819 ipc_port_t iport = thread->ith_thread_ports[THREAD_FLAVOR_INSPECT];
820 ipc_port_t rdport = thread->ith_thread_ports[THREAD_FLAVOR_READ];
821 ipc_port_t pport = thread->ith_self;
822
823 /*
824 * This innocuous looking line is load bearing.
825 *
826 * It is used to disable the creation of lazy made ports.
827 * We must do so before we drop the last reference on the thread,
828 * as thread ports do not own a reference on the thread, and
829 * convert_port_to_thread* will crash trying to resurect a thread.
830 */
831 thread->ipc_active = false;
1c79356b 832
0a7de745 833 if (kport != IP_NULL) {
c3c9b80d
A
834 ip_lock(kport);
835 kport->ip_alt_port = IP_NULL;
836 ipc_kobject_set_atomically(kport, IKO_NULL, IKOT_NONE);
837 ip_unlock(kport);
0a7de745 838 }
cb323159 839
f427ee49
A
840 if (iport != IP_NULL) {
841 ipc_kobject_set(iport, IKO_NULL, IKOT_NONE);
842 }
843
844 if (rdport != IP_NULL) {
845 ipc_kobject_set(rdport, IKO_NULL, IKOT_NONE);
846 }
847
c3c9b80d
A
848 if (pport != kport && pport != IP_NULL) {
849 assert(immovable_control_port_enabled);
850 assert(pport->ip_immovable_send);
851 ipc_kobject_set(pport, IKO_NULL, IKOT_NONE);
852 }
853
cb323159
A
854 /* unbind the thread special reply port */
855 if (IP_VALID(thread->ith_special_reply_port)) {
856 ipc_port_unbind_special_reply_port(thread, TRUE);
857 }
1c79356b
A
858}
859
860/*
91447636 861 * Routine: ipc_thread_terminate
1c79356b 862 * Purpose:
91447636 863 * Clean up and destroy a thread's IPC state.
1c79356b
A
864 * Conditions:
865 * Nothing locked.
866 */
867
868void
91447636 869ipc_thread_terminate(
0a7de745 870 thread_t thread)
1c79356b 871{
f427ee49
A
872 ipc_port_t kport = IP_NULL;
873 ipc_port_t iport = IP_NULL;
874 ipc_port_t rdport = IP_NULL;
875 ipc_port_t ith_rpc_reply = IP_NULL;
c3c9b80d 876 ipc_port_t pport = IP_NULL;
1c79356b 877
f427ee49 878 thread_mtx_lock(thread);
1c79356b 879
c3c9b80d
A
880 /*
881 * If we ever failed to clear ipc_active before the last reference
882 * was dropped, lazy ports might be made and used after the last
883 * reference is dropped and cause use after free (see comment in
884 * ipc_thread_disable()).
885 */
886 assert(!thread->ipc_active);
887
888 kport = thread->ith_thread_ports[THREAD_FLAVOR_CONTROL];
889 iport = thread->ith_thread_ports[THREAD_FLAVOR_INSPECT];
890 rdport = thread->ith_thread_ports[THREAD_FLAVOR_READ];
891 pport = thread->ith_self;
f427ee49
A
892
893 if (kport != IP_NULL) {
894 if (IP_VALID(thread->ith_settable_self)) {
895 ipc_port_release_send(thread->ith_settable_self);
0a7de745 896 }
1c79356b 897
c3c9b80d
A
898 thread->ith_thread_ports[THREAD_FLAVOR_CONTROL] = IP_NULL;
899 thread->ith_thread_ports[THREAD_FLAVOR_READ] = IP_NULL;
900 thread->ith_thread_ports[THREAD_FLAVOR_INSPECT] = IP_NULL;
901 thread->ith_settable_self = IP_NULL;
902 thread->ith_self = IP_NULL;
1c79356b 903
39236c6e 904 if (thread->exc_actions != NULL) {
f427ee49 905 for (int i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) {
0a7de745 906 if (IP_VALID(thread->exc_actions[i].port)) {
39236c6e 907 ipc_port_release_send(thread->exc_actions[i].port);
0a7de745 908 }
39236c6e
A
909 }
910 ipc_thread_destroy_exc_actions(thread);
911 }
1c79356b
A
912 }
913
39236c6e
A
914#if IMPORTANCE_INHERITANCE
915 assert(thread->ith_assertions == 0);
916#endif
917
91447636 918 assert(ipc_kmsg_queue_empty(&thread->ith_messages));
f427ee49
A
919 ith_rpc_reply = thread->ith_rpc_reply;
920 thread->ith_rpc_reply = IP_NULL;
1c79356b 921
f427ee49 922 thread_mtx_unlock(thread);
1c79356b 923
c3c9b80d
A
924 if (pport != kport && pport != IP_NULL) {
925 /* this thread has immovable contorl port */
926 ip_lock(kport);
927 kport->ip_alt_port = IP_NULL;
928 ipc_kobject_set_atomically(kport, IKO_NULL, IKOT_NONE);
929 ip_unlock(kport);
930 ipc_port_dealloc_kernel(pport);
931 }
f427ee49
A
932 if (kport != IP_NULL) {
933 ipc_port_dealloc_kernel(kport);
934 }
935 if (iport != IP_NULL) {
936 ipc_port_dealloc_kernel(iport);
937 }
938 if (rdport != IP_NULL) {
939 ipc_port_dealloc_kernel(rdport);
940 }
941 if (ith_rpc_reply != IP_NULL) {
942 ipc_port_dealloc_reply(ith_rpc_reply);
943 }
1c79356b
A
944}
945
6601e61a
A
946/*
947 * Routine: ipc_thread_reset
948 * Purpose:
949 * Reset the IPC state for a given Mach thread when
950 * its task enters an elevated security context.
f427ee49 951 * All flavors of thread port and its exception ports have
6601e61a 952 * to be reset. Its RPC reply port cannot have any
f427ee49
A
953 * rights outstanding, so it should be fine. The thread
954 * inspect and read port are set to NULL.
6601e61a
A
955 * Conditions:
956 * Nothing locked.
957 */
958
959void
960ipc_thread_reset(
0a7de745 961 thread_t thread)
6601e61a 962{
c3c9b80d 963 ipc_port_t old_kport, new_kport, old_pport, new_pport;
6601e61a 964 ipc_port_t old_sself;
f427ee49
A
965 ipc_port_t old_rdport;
966 ipc_port_t old_iport;
6601e61a 967 ipc_port_t old_exc_actions[EXC_TYPES_COUNT];
0a7de745 968 boolean_t has_old_exc_actions = FALSE;
c3c9b80d 969 boolean_t thread_is_immovable, thread_is_pinned;
f427ee49 970 int i;
6601e61a 971
5ba3f43e
A
972#if CONFIG_MACF
973 struct label *new_label = mac_exc_create_label();
974#endif
0a7de745 975
c3c9b80d
A
976 thread_is_immovable = thread->ith_self->ip_immovable_send;
977 thread_is_pinned = thread->ith_self->ip_pinned;
978
979 if (thread_is_immovable) {
980 ipc_kobject_alloc_options_t alloc_options = IPC_KOBJECT_ALLOC_NONE;
981
982 if (thread_is_pinned) {
983 assert(pinned_control_port_enabled);
984 alloc_options |= IPC_KOBJECT_ALLOC_PINNED;
985 }
986 if (thread_is_immovable) {
987 alloc_options |= IPC_KOBJECT_ALLOC_IMMOVABLE_SEND;
988 }
989 new_pport = ipc_kobject_alloc_port((ipc_kobject_t)thread,
990 IKOT_THREAD_CONTROL, alloc_options);
991
992 new_kport = ipc_kobject_alloc_labeled_port((ipc_kobject_t)thread,
993 IKOT_THREAD_CONTROL, IPC_LABEL_SUBST_THREAD,
994 IPC_KOBJECT_ALLOC_NONE);
995 new_kport->ip_alt_port = new_pport;
996 } else {
997 new_kport = ipc_kobject_alloc_port((ipc_kobject_t)thread,
998 IKOT_THREAD_CONTROL, IPC_KOBJECT_ALLOC_NONE);
999
1000 new_pport = new_kport;
1001 }
6601e61a
A
1002
1003 thread_mtx_lock(thread);
1004
c3c9b80d
A
1005 old_kport = thread->ith_thread_ports[THREAD_FLAVOR_CONTROL];
1006 old_rdport = thread->ith_thread_ports[THREAD_FLAVOR_READ];
1007 old_iport = thread->ith_thread_ports[THREAD_FLAVOR_INSPECT];
1008
f427ee49 1009 old_sself = thread->ith_settable_self;
c3c9b80d 1010 old_pport = thread->ith_self;
6601e61a 1011
3e170ce0 1012 if (old_kport == IP_NULL && thread->inspection == FALSE) {
c3c9b80d 1013 /* thread is already terminated (can this happen?) */
6601e61a
A
1014 thread_mtx_unlock(thread);
1015 ipc_port_dealloc_kernel(new_kport);
c3c9b80d
A
1016 if (thread_is_immovable) {
1017 ipc_port_dealloc_kernel(new_pport);
1018 }
5ba3f43e
A
1019#if CONFIG_MACF
1020 mac_exc_free_label(new_label);
1021#endif
6601e61a
A
1022 return;
1023 }
1024
c3c9b80d
A
1025 thread->ipc_active = true;
1026 thread->ith_thread_ports[THREAD_FLAVOR_CONTROL] = new_kport;
1027 thread->ith_self = new_pport;
1028 thread->ith_settable_self = ipc_port_make_send(new_kport);
1029 thread->ith_thread_ports[THREAD_FLAVOR_INSPECT] = IP_NULL;
1030 thread->ith_thread_ports[THREAD_FLAVOR_READ] = IP_NULL;
f427ee49 1031
3e170ce0 1032 if (old_kport != IP_NULL) {
c3c9b80d
A
1033 ip_lock(old_kport);
1034 old_kport->ip_alt_port = IP_NULL;
1035 ipc_kobject_set_atomically(old_kport, IKO_NULL, IKOT_NONE);
1036 ip_unlock(old_kport);
3e170ce0 1037 }
f427ee49
A
1038 if (old_rdport != IP_NULL) {
1039 ipc_kobject_set(old_rdport, IKO_NULL, IKOT_NONE);
1040 }
1041 if (old_iport != IP_NULL) {
1042 ipc_kobject_set(old_iport, IKO_NULL, IKOT_NONE);
1043 }
c3c9b80d
A
1044 if (thread_is_immovable && old_pport != IP_NULL) {
1045 ipc_kobject_set(old_pport, IKO_NULL, IKOT_NONE);
1046 }
6601e61a 1047
39236c6e
A
1048 /*
1049 * Only ports that were set by root-owned processes
0a7de745 1050 * (privileged ports) should survive
39236c6e
A
1051 */
1052 if (thread->exc_actions != NULL) {
1053 has_old_exc_actions = TRUE;
1054 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1055 if (thread->exc_actions[i].privileged) {
1056 old_exc_actions[i] = IP_NULL;
1057 } else {
39037602 1058#if CONFIG_MACF
5ba3f43e 1059 mac_exc_update_action_label(thread->exc_actions + i, new_label);
39037602 1060#endif
39236c6e 1061 old_exc_actions[i] = thread->exc_actions[i].port;
0a7de745 1062 thread->exc_actions[i].port = IP_NULL;
39236c6e 1063 }
6601e61a 1064 }
39236c6e 1065 }
6601e61a
A
1066
1067 thread_mtx_unlock(thread);
1068
5ba3f43e
A
1069#if CONFIG_MACF
1070 mac_exc_free_label(new_label);
1071#endif
0a7de745 1072
6601e61a
A
1073 /* release the naked send rights */
1074
0a7de745 1075 if (IP_VALID(old_sself)) {
6601e61a 1076 ipc_port_release_send(old_sself);
0a7de745 1077 }
6601e61a 1078
39236c6e
A
1079 if (has_old_exc_actions) {
1080 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
6601e61a
A
1081 ipc_port_release_send(old_exc_actions[i]);
1082 }
39236c6e 1083 }
6601e61a
A
1084
1085 /* destroy the kernel port */
3e170ce0
A
1086 if (old_kport != IP_NULL) {
1087 ipc_port_dealloc_kernel(old_kport);
1088 }
f427ee49
A
1089 if (old_rdport != IP_NULL) {
1090 ipc_port_dealloc_kernel(old_rdport);
1091 }
1092 if (old_iport != IP_NULL) {
1093 ipc_port_dealloc_kernel(old_iport);
1094 }
5ba3f43e 1095
c3c9b80d
A
1096 if (thread_is_immovable && old_pport != IP_NULL) {
1097 ipc_port_dealloc_kernel(old_pport);
1098 }
1099
5ba3f43e
A
1100 /* unbind the thread special reply port */
1101 if (IP_VALID(thread->ith_special_reply_port)) {
1102 ipc_port_unbind_special_reply_port(thread, TRUE);
1103 }
6601e61a
A
1104}
1105
1c79356b
A
1106/*
1107 * Routine: retrieve_task_self_fast
1108 * Purpose:
1109 * Optimized version of retrieve_task_self,
1110 * that only works for the current task.
1111 *
1112 * Return a send right (possibly null/dead)
1113 * for the task's user-visible self port.
1114 * Conditions:
1115 * Nothing locked.
1116 */
1117
1118ipc_port_t
1119retrieve_task_self_fast(
0a7de745 1120 task_t task)
1c79356b 1121{
c3c9b80d 1122 ipc_port_t port = IP_NULL;
1c79356b
A
1123
1124 assert(task == current_task());
1125
1126 itk_lock(task);
c3c9b80d
A
1127 assert(task->itk_self != IP_NULL);
1128
1129 if (task->itk_settable_self == task->itk_task_ports[TASK_FLAVOR_CONTROL]) {
1130 /* no interposing, return the IMMOVABLE port */
1131 port = ipc_port_make_send(task->itk_self);
1132 if (immovable_control_port_enabled) {
1133 assert(port->ip_immovable_send == 1);
1134 if (pinned_control_port_enabled) {
1135 /* pinned port is also immovable */
1136 assert(port->ip_pinned == 1);
1137 }
1138 }
0a7de745 1139 } else {
c3c9b80d 1140 port = ipc_port_copy_send(task->itk_settable_self);
0a7de745 1141 }
1c79356b
A
1142 itk_unlock(task);
1143
1144 return port;
1145}
1146
c3c9b80d
A
1147/*
1148 * Routine: mach_task_is_self
1149 * Purpose:
1150 * [MIG call] Checks if the task (control/read/inspect/name/movable)
1151 * port is pointing to current_task.
1152 */
1153kern_return_t
1154mach_task_is_self(
1155 task_t task,
1156 boolean_t *is_self)
1157{
1158 if (task == TASK_NULL) {
1159 return KERN_INVALID_ARGUMENT;
1160 }
1161
1162 *is_self = (task == current_task());
1163
1164 return KERN_SUCCESS;
1165}
1166
1c79356b 1167/*
91447636 1168 * Routine: retrieve_thread_self_fast
1c79356b 1169 * Purpose:
1c79356b
A
1170 * Return a send right (possibly null/dead)
1171 * for the thread's user-visible self port.
91447636
A
1172 *
1173 * Only works for the current thread.
1174 *
1c79356b
A
1175 * Conditions:
1176 * Nothing locked.
1177 */
1178
1179ipc_port_t
91447636 1180retrieve_thread_self_fast(
0a7de745 1181 thread_t thread)
1c79356b 1182{
c3c9b80d 1183 ipc_port_t port = IP_NULL;
1c79356b 1184
91447636
A
1185 assert(thread == current_thread());
1186
1187 thread_mtx_lock(thread);
1c79356b 1188
c3c9b80d 1189 assert(thread->ith_self != IP_NULL);
91447636 1190
c3c9b80d
A
1191 if (thread->ith_settable_self == thread->ith_thread_ports[THREAD_FLAVOR_CONTROL]) {
1192 /* no interposing, return IMMOVABLE_PORT */
1193 port = ipc_port_make_send(thread->ith_self);
0a7de745 1194 } else {
c3c9b80d 1195 port = ipc_port_copy_send(thread->ith_settable_self);
0a7de745 1196 }
91447636
A
1197
1198 thread_mtx_unlock(thread);
1c79356b
A
1199
1200 return port;
1201}
1202
1203/*
1204 * Routine: task_self_trap [mach trap]
1205 * Purpose:
1206 * Give the caller send rights for his own task port.
1207 * Conditions:
1208 * Nothing locked.
1209 * Returns:
1210 * MACH_PORT_NULL if there are any resource failures
1211 * or other errors.
1212 */
1213
1214mach_port_name_t
91447636
A
1215task_self_trap(
1216 __unused struct task_self_trap_args *args)
1c79356b
A
1217{
1218 task_t task = current_task();
1219 ipc_port_t sright;
91447636 1220 mach_port_name_t name;
1c79356b
A
1221
1222 sright = retrieve_task_self_fast(task);
91447636
A
1223 name = ipc_port_copyout_send(sright, task->itk_space);
1224 return name;
1c79356b
A
1225}
1226
1227/*
1228 * Routine: thread_self_trap [mach trap]
1229 * Purpose:
1230 * Give the caller send rights for his own thread port.
1231 * Conditions:
1232 * Nothing locked.
1233 * Returns:
1234 * MACH_PORT_NULL if there are any resource failures
1235 * or other errors.
1236 */
1237
1238mach_port_name_t
91447636
A
1239thread_self_trap(
1240 __unused struct thread_self_trap_args *args)
1c79356b 1241{
91447636
A
1242 thread_t thread = current_thread();
1243 task_t task = thread->task;
1c79356b 1244 ipc_port_t sright;
91447636
A
1245 mach_port_name_t name;
1246
1247 sright = retrieve_thread_self_fast(thread);
1248 name = ipc_port_copyout_send(sright, task->itk_space);
1249 return name;
1c79356b
A
1250}
1251
1252/*
1253 * Routine: mach_reply_port [mach trap]
1254 * Purpose:
1255 * Allocate a port for the caller.
1256 * Conditions:
1257 * Nothing locked.
1258 * Returns:
1259 * MACH_PORT_NULL if there are any resource failures
1260 * or other errors.
1261 */
1262
1263mach_port_name_t
91447636
A
1264mach_reply_port(
1265 __unused struct mach_reply_port_args *args)
1c79356b
A
1266{
1267 ipc_port_t port;
1268 mach_port_name_t name;
1269 kern_return_t kr;
1270
94ff46dc
A
1271 kr = ipc_port_alloc(current_task()->itk_space, IPC_PORT_INIT_MESSAGE_QUEUE,
1272 &name, &port);
0a7de745 1273 if (kr == KERN_SUCCESS) {
1c79356b 1274 ip_unlock(port);
0a7de745 1275 } else {
1c79356b 1276 name = MACH_PORT_NULL;
0a7de745 1277 }
1c79356b
A
1278 return name;
1279}
1280
5ba3f43e
A
1281/*
1282 * Routine: thread_get_special_reply_port [mach trap]
1283 * Purpose:
1284 * Allocate a special reply port for the calling thread.
1285 * Conditions:
1286 * Nothing locked.
1287 * Returns:
d9a64523 1288 * mach_port_name_t: send right & receive right for special reply port.
5ba3f43e
A
1289 * MACH_PORT_NULL if there are any resource failures
1290 * or other errors.
1291 */
1292
1293mach_port_name_t
1294thread_get_special_reply_port(
1295 __unused struct thread_get_special_reply_port_args *args)
1296{
1297 ipc_port_t port;
1298 mach_port_name_t name;
1299 kern_return_t kr;
1300 thread_t thread = current_thread();
94ff46dc
A
1301 ipc_port_init_flags_t flags = IPC_PORT_INIT_MESSAGE_QUEUE |
1302 IPC_PORT_INIT_MAKE_SEND_RIGHT | IPC_PORT_INIT_SPECIAL_REPLY;
5ba3f43e
A
1303
1304 /* unbind the thread special reply port */
1305 if (IP_VALID(thread->ith_special_reply_port)) {
1306 kr = ipc_port_unbind_special_reply_port(thread, TRUE);
1307 if (kr != KERN_SUCCESS) {
1308 return MACH_PORT_NULL;
1309 }
1310 }
1311
94ff46dc 1312 kr = ipc_port_alloc(current_task()->itk_space, flags, &name, &port);
5ba3f43e
A
1313 if (kr == KERN_SUCCESS) {
1314 ipc_port_bind_special_reply_port_locked(port);
1315 ip_unlock(port);
1316 } else {
1317 name = MACH_PORT_NULL;
1318 }
1319 return name;
1320}
1321
1322/*
1323 * Routine: ipc_port_bind_special_reply_port_locked
1324 * Purpose:
1325 * Bind the given port to current thread as a special reply port.
1326 * Conditions:
1327 * Port locked.
1328 * Returns:
1329 * None.
1330 */
1331
1332static void
1333ipc_port_bind_special_reply_port_locked(
1334 ipc_port_t port)
1335{
1336 thread_t thread = current_thread();
1337 assert(thread->ith_special_reply_port == NULL);
94ff46dc
A
1338 assert(port->ip_specialreply);
1339 assert(port->ip_sync_link_state == PORT_SYNC_LINK_ANY);
5ba3f43e
A
1340
1341 ip_reference(port);
1342 thread->ith_special_reply_port = port;
cb323159 1343 port->ip_messages.imq_srp_owner_thread = thread;
d9a64523 1344
cb323159 1345 ipc_special_reply_port_bits_reset(port);
5ba3f43e
A
1346}
1347
1348/*
1349 * Routine: ipc_port_unbind_special_reply_port
1350 * Purpose:
1351 * Unbind the thread's special reply port.
d9a64523
A
1352 * If the special port has threads waiting on turnstile,
1353 * update it's inheritor.
5ba3f43e
A
1354 * Condition:
1355 * Nothing locked.
1356 * Returns:
1357 * None.
1358 */
1359static kern_return_t
1360ipc_port_unbind_special_reply_port(
1361 thread_t thread,
1362 boolean_t unbind_active_port)
1363{
1364 ipc_port_t special_reply_port = thread->ith_special_reply_port;
1365
1366 ip_lock(special_reply_port);
1367
1368 /* Return error if port active and unbind_active_port set to FALSE */
1369 if (unbind_active_port == FALSE && ip_active(special_reply_port)) {
1370 ip_unlock(special_reply_port);
1371 return KERN_FAILURE;
1372 }
1373
1374 thread->ith_special_reply_port = NULL;
d9a64523 1375 ipc_port_adjust_special_reply_port_locked(special_reply_port, NULL,
cb323159 1376 IPC_PORT_ADJUST_UNLINK_THREAD, FALSE);
5ba3f43e
A
1377 /* port unlocked */
1378
1379 ip_release(special_reply_port);
1380 return KERN_SUCCESS;
1381}
1382
91447636
A
1383/*
1384 * Routine: thread_get_special_port [kernel call]
1385 * Purpose:
1386 * Clones a send right for one of the thread's
1387 * special ports.
1388 * Conditions:
1389 * Nothing locked.
1390 * Returns:
1391 * KERN_SUCCESS Extracted a send right.
1392 * KERN_INVALID_ARGUMENT The thread is null.
1393 * KERN_FAILURE The thread is dead.
1394 * KERN_INVALID_ARGUMENT Invalid special port.
1395 */
1396
1397kern_return_t
1398thread_get_special_port(
f427ee49
A
1399 thread_inspect_t thread,
1400 int which,
1401 ipc_port_t *portp);
1402
c3c9b80d 1403static kern_return_t
f427ee49
A
1404thread_get_special_port_internal(
1405 thread_inspect_t thread,
1406 int which,
1407 ipc_port_t *portp,
1408 mach_thread_flavor_t flavor)
91447636 1409{
f427ee49
A
1410 kern_return_t kr;
1411 ipc_port_t port;
91447636 1412
0a7de745
A
1413 if (thread == THREAD_NULL) {
1414 return KERN_INVALID_ARGUMENT;
1415 }
91447636 1416
f427ee49
A
1417 if ((kr = port_allowed_with_thread_flavor(which, flavor)) != KERN_SUCCESS) {
1418 return kr;
1419 }
1420
1421 thread_mtx_lock(thread);
1422 if (!thread->active) {
1423 thread_mtx_unlock(thread);
1424 return KERN_FAILURE;
1425 }
1426
91447636 1427 switch (which) {
91447636 1428 case THREAD_KERNEL_PORT:
f427ee49
A
1429 port = ipc_port_copy_send(thread->ith_settable_self);
1430 thread_mtx_unlock(thread);
1431 break;
1432
1433 case THREAD_READ_PORT:
1434 case THREAD_INSPECT_PORT:
1435 thread_mtx_unlock(thread);
1436 mach_thread_flavor_t current_flavor = (which == THREAD_READ_PORT) ?
1437 THREAD_FLAVOR_READ : THREAD_FLAVOR_INSPECT;
1438 /* convert_thread_to_port_with_flavor consumes a thread reference */
1439 thread_reference(thread);
1440 port = convert_thread_to_port_with_flavor(thread, current_flavor);
91447636
A
1441 break;
1442
1443 default:
f427ee49 1444 thread_mtx_unlock(thread);
0a7de745 1445 return KERN_INVALID_ARGUMENT;
91447636
A
1446 }
1447
f427ee49 1448 *portp = port;
f427ee49
A
1449 return KERN_SUCCESS;
1450}
1451
1452kern_return_t
1453thread_get_special_port(
1454 thread_inspect_t thread,
1455 int which,
1456 ipc_port_t *portp)
1457{
1458 return thread_get_special_port_internal(thread, which, portp, THREAD_FLAVOR_CONTROL);
1459}
1460
c3c9b80d
A
1461static ipc_port_t
1462thread_get_non_substituted_self(thread_t thread)
1463{
1464 ipc_port_t port = IP_NULL;
1465
1466 thread_mtx_lock(thread);
1467 port = thread->ith_settable_self;
1468 if (IP_VALID(port)) {
1469 ip_reference(port);
1470 }
1471 thread_mtx_unlock(thread);
1472
1473 if (IP_VALID(port)) {
1474 /* consumes the port reference */
1475 return ipc_kobject_alloc_subst_once(port);
1476 }
1477
1478 return port;
1479}
1480
f427ee49
A
1481kern_return_t
1482thread_get_special_port_from_user(
1483 mach_port_t port,
1484 int which,
1485 ipc_port_t *portp)
1486{
1487 ipc_kobject_type_t kotype;
c3c9b80d
A
1488 mach_thread_flavor_t flavor;
1489 kern_return_t kr = KERN_SUCCESS;
f427ee49 1490
c3c9b80d
A
1491 thread_t thread = convert_port_to_thread_check_type(port, &kotype,
1492 THREAD_FLAVOR_INSPECT, FALSE);
f427ee49
A
1493
1494 if (thread == THREAD_NULL) {
1495 return KERN_INVALID_ARGUMENT;
0a7de745 1496 }
91447636 1497
c3c9b80d
A
1498 if (which == THREAD_KERNEL_PORT && thread->task == current_task()) {
1499#if CONFIG_MACF
1500 /*
1501 * only check for threads belong to current_task,
1502 * because foreign thread ports are always movable
1503 */
1504 if (mac_task_check_get_movable_control_port()) {
1505 kr = KERN_DENIED;
1506 goto out;
1507 }
1508#endif
1509 if (kotype == IKOT_THREAD_CONTROL) {
1510 *portp = thread_get_non_substituted_self(thread);
1511 goto out;
1512 }
1513 }
1514
f427ee49
A
1515 switch (kotype) {
1516 case IKOT_THREAD_CONTROL:
c3c9b80d 1517 flavor = THREAD_FLAVOR_CONTROL;
f427ee49
A
1518 break;
1519 case IKOT_THREAD_READ:
c3c9b80d 1520 flavor = THREAD_FLAVOR_READ;
f427ee49
A
1521 break;
1522 case IKOT_THREAD_INSPECT:
c3c9b80d 1523 flavor = THREAD_FLAVOR_INSPECT;
f427ee49
A
1524 break;
1525 default:
1526 panic("strange kobject type");
f427ee49 1527 }
91447636 1528
c3c9b80d
A
1529 kr = thread_get_special_port_internal(thread, which, portp, flavor);
1530out:
f427ee49
A
1531 thread_deallocate(thread);
1532 return kr;
1533}
1534
1535static kern_return_t
1536port_allowed_with_thread_flavor(
1537 int which,
1538 mach_thread_flavor_t flavor)
1539{
1540 switch (flavor) {
1541 case THREAD_FLAVOR_CONTROL:
1542 return KERN_SUCCESS;
1543
1544 case THREAD_FLAVOR_READ:
1545
1546 switch (which) {
1547 case THREAD_READ_PORT:
1548 case THREAD_INSPECT_PORT:
1549 return KERN_SUCCESS;
1550 default:
1551 return KERN_INVALID_CAPABILITY;
1552 }
1553
1554 case THREAD_FLAVOR_INSPECT:
1555
1556 switch (which) {
1557 case THREAD_INSPECT_PORT:
1558 return KERN_SUCCESS;
1559 default:
1560 return KERN_INVALID_CAPABILITY;
1561 }
1562
1563 default:
1564 return KERN_INVALID_CAPABILITY;
1565 }
91447636
A
1566}
1567
1568/*
1569 * Routine: thread_set_special_port [kernel call]
1570 * Purpose:
1571 * Changes one of the thread's special ports,
1572 * setting it to the supplied send right.
1573 * Conditions:
1574 * Nothing locked. If successful, consumes
1575 * the supplied send right.
1576 * Returns:
f427ee49
A
1577 * KERN_SUCCESS Changed the special port.
1578 * KERN_INVALID_ARGUMENT The thread is null.
c3c9b80d 1579 * KERN_INVALID_RIGHT Port is marked as immovable.
f427ee49
A
1580 * KERN_FAILURE The thread is dead.
1581 * KERN_INVALID_ARGUMENT Invalid special port.
1582 * KERN_NO_ACCESS Restricted access to set port.
91447636
A
1583 */
1584
1585kern_return_t
1586thread_set_special_port(
0a7de745
A
1587 thread_t thread,
1588 int which,
c3c9b80d 1589 ipc_port_t port)
91447636 1590{
0a7de745
A
1591 kern_return_t result = KERN_SUCCESS;
1592 ipc_port_t *whichp, old = IP_NULL;
91447636 1593
0a7de745
A
1594 if (thread == THREAD_NULL) {
1595 return KERN_INVALID_ARGUMENT;
1596 }
91447636 1597
c3c9b80d
A
1598 if (IP_VALID(port) && (port->ip_immovable_receive || port->ip_immovable_send)) {
1599 return KERN_INVALID_RIGHT;
1600 }
1601
91447636 1602 switch (which) {
91447636 1603 case THREAD_KERNEL_PORT:
f427ee49
A
1604#if CONFIG_CSR
1605 if (csr_check(CSR_ALLOW_KERNEL_DEBUGGER) != 0) {
1606 /*
1607 * Only allow setting of thread-self
1608 * special port from user-space when SIP is
1609 * disabled (for Mach-on-Mach emulation).
1610 */
1611 return KERN_NO_ACCESS;
1612 }
1613#endif
1614 whichp = &thread->ith_settable_self;
91447636
A
1615 break;
1616
1617 default:
0a7de745 1618 return KERN_INVALID_ARGUMENT;
91447636
A
1619 }
1620
1621 thread_mtx_lock(thread);
1622
1623 if (thread->active) {
1624 old = *whichp;
1625 *whichp = port;
0a7de745 1626 } else {
91447636 1627 result = KERN_FAILURE;
0a7de745 1628 }
91447636
A
1629
1630 thread_mtx_unlock(thread);
1631
0a7de745 1632 if (IP_VALID(old)) {
91447636 1633 ipc_port_release_send(old);
0a7de745 1634 }
91447636 1635
0a7de745 1636 return result;
91447636
A
1637}
1638
1c79356b
A
1639/*
1640 * Routine: task_get_special_port [kernel call]
1641 * Purpose:
1642 * Clones a send right for one of the task's
1643 * special ports.
1644 * Conditions:
1645 * Nothing locked.
1646 * Returns:
c3c9b80d 1647 * KERN_SUCCESS Extracted a send right.
1c79356b 1648 * KERN_INVALID_ARGUMENT The task is null.
c3c9b80d 1649 * KERN_FAILURE The task/space is dead.
1c79356b
A
1650 * KERN_INVALID_ARGUMENT Invalid special port.
1651 */
1652
1653kern_return_t
1654task_get_special_port(
0a7de745
A
1655 task_t task,
1656 int which,
f427ee49
A
1657 ipc_port_t *portp);
1658
1659static kern_return_t
1660task_get_special_port_internal(
1661 task_t task,
1662 int which,
1663 ipc_port_t *portp,
1664 mach_task_flavor_t flavor)
1c79356b 1665{
f427ee49 1666 kern_return_t kr;
1c79356b
A
1667 ipc_port_t port;
1668
0a7de745 1669 if (task == TASK_NULL) {
1c79356b 1670 return KERN_INVALID_ARGUMENT;
0a7de745 1671 }
1c79356b 1672
f427ee49
A
1673 if ((kr = port_allowed_with_task_flavor(which, flavor)) != KERN_SUCCESS) {
1674 return kr;
1675 }
1676
0c530ab8 1677 itk_lock(task);
c3c9b80d 1678 if (!task->ipc_active) {
0c530ab8
A
1679 itk_unlock(task);
1680 return KERN_FAILURE;
1681 }
1682
1c79356b 1683 switch (which) {
39037602 1684 case TASK_KERNEL_PORT:
f427ee49 1685 port = ipc_port_copy_send(task->itk_settable_self);
c3c9b80d 1686 itk_unlock(task);
0c530ab8
A
1687 break;
1688
f427ee49
A
1689 case TASK_READ_PORT:
1690 case TASK_INSPECT_PORT:
1691 itk_unlock(task);
1692 mach_task_flavor_t current_flavor = (which == TASK_READ_PORT) ?
1693 TASK_FLAVOR_READ : TASK_FLAVOR_INSPECT;
1694 /* convert_task_to_port_with_flavor consumes a task reference */
1695 task_reference(task);
1696 port = convert_task_to_port_with_flavor(task, current_flavor);
c3c9b80d 1697 break;
f427ee49 1698
39037602 1699 case TASK_NAME_PORT:
c3c9b80d
A
1700 port = ipc_port_make_send(task->itk_task_ports[TASK_FLAVOR_NAME]);
1701 itk_unlock(task);
1c79356b
A
1702 break;
1703
39037602 1704 case TASK_HOST_PORT:
0c530ab8 1705 port = ipc_port_copy_send(task->itk_host);
c3c9b80d 1706 itk_unlock(task);
1c79356b
A
1707 break;
1708
39037602 1709 case TASK_BOOTSTRAP_PORT:
0c530ab8 1710 port = ipc_port_copy_send(task->itk_bootstrap);
c3c9b80d 1711 itk_unlock(task);
1c79356b
A
1712 break;
1713
39037602 1714 case TASK_SEATBELT_PORT:
2d21ac55 1715 port = ipc_port_copy_send(task->itk_seatbelt);
c3c9b80d 1716 itk_unlock(task);
2d21ac55
A
1717 break;
1718
39037602 1719 case TASK_ACCESS_PORT:
2d21ac55 1720 port = ipc_port_copy_send(task->itk_task_access);
c3c9b80d 1721 itk_unlock(task);
2d21ac55 1722 break;
39236c6e 1723
39037602 1724 case TASK_DEBUG_CONTROL_PORT:
fe8ab488 1725 port = ipc_port_copy_send(task->itk_debug_control);
c3c9b80d 1726 itk_unlock(task);
fe8ab488
A
1727 break;
1728
39037602 1729 default:
0a7de745 1730 itk_unlock(task);
1c79356b
A
1731 return KERN_INVALID_ARGUMENT;
1732 }
f427ee49 1733
1c79356b
A
1734 *portp = port;
1735 return KERN_SUCCESS;
1736}
1737
f427ee49
A
1738kern_return_t
1739task_get_special_port(
1740 task_t task,
1741 int which,
1742 ipc_port_t *portp)
1743{
1744 return task_get_special_port_internal(task, which, portp, TASK_FLAVOR_CONTROL);
1745}
1746
c3c9b80d
A
1747static ipc_port_t
1748task_get_non_substituted_self(task_t task)
1749{
1750 ipc_port_t port = IP_NULL;
1751
1752 itk_lock(task);
1753 port = task->itk_settable_self;
1754 if (IP_VALID(port)) {
1755 ip_reference(port);
1756 }
1757 itk_unlock(task);
1758
1759 if (IP_VALID(port)) {
1760 /* consumes the port reference */
1761 return ipc_kobject_alloc_subst_once(port);
1762 }
1763
1764 return port;
1765}
f427ee49
A
1766kern_return_t
1767task_get_special_port_from_user(
1768 mach_port_t port,
1769 int which,
1770 ipc_port_t *portp)
1771{
1772 ipc_kobject_type_t kotype;
c3c9b80d
A
1773 mach_task_flavor_t flavor;
1774 kern_return_t kr = KERN_SUCCESS;
f427ee49 1775
c3c9b80d
A
1776 task_t task = convert_port_to_task_check_type(port, &kotype,
1777 TASK_FLAVOR_INSPECT, FALSE);
f427ee49
A
1778
1779 if (task == TASK_NULL) {
1780 return KERN_INVALID_ARGUMENT;
1781 }
1782
c3c9b80d
A
1783 if (which == TASK_KERNEL_PORT && task == current_task()) {
1784#if CONFIG_MACF
1785 /*
1786 * only check for current_task,
1787 * because foreign task ports are always movable
1788 */
1789 if (mac_task_check_get_movable_control_port()) {
1790 kr = KERN_DENIED;
1791 goto out;
1792 }
1793#endif
1794 if (kotype == IKOT_TASK_CONTROL) {
1795 *portp = task_get_non_substituted_self(task);
1796 goto out;
1797 }
1798 }
1799
f427ee49
A
1800 switch (kotype) {
1801 case IKOT_TASK_CONTROL:
c3c9b80d 1802 flavor = TASK_FLAVOR_CONTROL;
f427ee49
A
1803 break;
1804 case IKOT_TASK_READ:
c3c9b80d 1805 flavor = TASK_FLAVOR_READ;
f427ee49
A
1806 break;
1807 case IKOT_TASK_INSPECT:
c3c9b80d 1808 flavor = TASK_FLAVOR_INSPECT;
f427ee49
A
1809 break;
1810 default:
1811 panic("strange kobject type");
f427ee49
A
1812 }
1813
c3c9b80d
A
1814 kr = task_get_special_port_internal(task, which, portp, flavor);
1815out:
f427ee49
A
1816 task_deallocate(task);
1817 return kr;
1818}
1819
1820static kern_return_t
1821port_allowed_with_task_flavor(
1822 int which,
1823 mach_task_flavor_t flavor)
1824{
1825 switch (flavor) {
1826 case TASK_FLAVOR_CONTROL:
1827 return KERN_SUCCESS;
1828
1829 case TASK_FLAVOR_READ:
1830
1831 switch (which) {
1832 case TASK_READ_PORT:
1833 case TASK_INSPECT_PORT:
1834 case TASK_NAME_PORT:
1835 return KERN_SUCCESS;
1836 default:
1837 return KERN_INVALID_CAPABILITY;
1838 }
1839
1840 case TASK_FLAVOR_INSPECT:
1841
1842 switch (which) {
1843 case TASK_INSPECT_PORT:
1844 case TASK_NAME_PORT:
1845 return KERN_SUCCESS;
1846 default:
1847 return KERN_INVALID_CAPABILITY;
1848 }
1849
1850 default:
1851 return KERN_INVALID_CAPABILITY;
1852 }
1853}
1854
1c79356b
A
1855/*
1856 * Routine: task_set_special_port [kernel call]
1857 * Purpose:
1858 * Changes one of the task's special ports,
1859 * setting it to the supplied send right.
1860 * Conditions:
1861 * Nothing locked. If successful, consumes
1862 * the supplied send right.
1863 * Returns:
c3c9b80d 1864 * KERN_SUCCESS Changed the special port.
1c79356b 1865 * KERN_INVALID_ARGUMENT The task is null.
c3c9b80d
A
1866 * KERN_INVALID_RIGHT Port is marked as immovable.
1867 * KERN_FAILURE The task/space is dead.
1c79356b 1868 * KERN_INVALID_ARGUMENT Invalid special port.
c3c9b80d 1869 * KERN_NO_ACCESS Restricted access to set port.
1c79356b
A
1870 */
1871
1872kern_return_t
1873task_set_special_port(
0a7de745
A
1874 task_t task,
1875 int which,
1876 ipc_port_t port)
1c79356b 1877{
0a7de745 1878 if (task == TASK_NULL) {
1c79356b 1879 return KERN_INVALID_ARGUMENT;
0a7de745 1880 }
1c79356b 1881
cb323159
A
1882 if (task_is_driver(current_task())) {
1883 return KERN_NO_ACCESS;
1884 }
1885
c3c9b80d
A
1886 if (IP_VALID(port) && (port->ip_immovable_receive || port->ip_immovable_send)) {
1887 return KERN_INVALID_RIGHT;
1888 }
1889
4ba76501
A
1890 switch (which) {
1891 case TASK_KERNEL_PORT:
1892 case TASK_HOST_PORT:
1893#if CONFIG_CSR
1894 if (csr_check(CSR_ALLOW_KERNEL_DEBUGGER) == 0) {
1895 /*
1896 * Only allow setting of task-self / task-host
1897 * special ports from user-space when SIP is
1898 * disabled (for Mach-on-Mach emulation).
1899 */
1900 break;
1901 }
1902#endif
1903 return KERN_NO_ACCESS;
1904 default:
1905 break;
1906 }
1907
1908 return task_set_special_port_internal(task, which, port);
1909}
1910
1911/*
1912 * Routine: task_set_special_port_internal
1913 * Purpose:
1914 * Changes one of the task's special ports,
1915 * setting it to the supplied send right.
1916 * Conditions:
1917 * Nothing locked. If successful, consumes
1918 * the supplied send right.
1919 * Returns:
1920 * KERN_SUCCESS Changed the special port.
1921 * KERN_INVALID_ARGUMENT The task is null.
1922 * KERN_FAILURE The task/space is dead.
1923 * KERN_INVALID_ARGUMENT Invalid special port.
1924 * KERN_NO_ACCESS Restricted access to overwrite port.
1925 */
1926
1927kern_return_t
1928task_set_special_port_internal(
1929 task_t task,
1930 int which,
1931 ipc_port_t port)
1932{
f427ee49
A
1933 ipc_port_t old = IP_NULL;
1934 kern_return_t rc = KERN_INVALID_ARGUMENT;
4ba76501
A
1935
1936 if (task == TASK_NULL) {
f427ee49
A
1937 goto out;
1938 }
1939
1940 itk_lock(task);
c3c9b80d 1941 if (!task->ipc_active) {
f427ee49
A
1942 rc = KERN_FAILURE;
1943 goto out_unlock;
4ba76501
A
1944 }
1945
1c79356b 1946 switch (which) {
39037602 1947 case TASK_KERNEL_PORT:
f427ee49
A
1948 old = task->itk_settable_self;
1949 task->itk_settable_self = port;
0a7de745 1950 break;
1c79356b 1951
39037602 1952 case TASK_HOST_PORT:
f427ee49
A
1953 old = task->itk_host;
1954 task->itk_host = port;
0a7de745 1955 break;
1c79356b 1956
39037602 1957 case TASK_BOOTSTRAP_PORT:
f427ee49
A
1958 old = task->itk_bootstrap;
1959 task->itk_bootstrap = port;
0a7de745 1960 break;
1c79356b 1961
f427ee49 1962 /* Never allow overwrite of seatbelt port */
39037602 1963 case TASK_SEATBELT_PORT:
f427ee49
A
1964 if (IP_VALID(task->itk_seatbelt)) {
1965 rc = KERN_NO_ACCESS;
1966 goto out_unlock;
1967 }
1968 task->itk_seatbelt = port;
0a7de745 1969 break;
2d21ac55 1970
f427ee49 1971 /* Never allow overwrite of the task access port */
39037602 1972 case TASK_ACCESS_PORT:
f427ee49
A
1973 if (IP_VALID(task->itk_task_access)) {
1974 rc = KERN_NO_ACCESS;
1975 goto out_unlock;
1976 }
1977 task->itk_task_access = port;
0a7de745 1978 break;
fe8ab488 1979
39037602 1980 case TASK_DEBUG_CONTROL_PORT:
f427ee49
A
1981 old = task->itk_debug_control;
1982 task->itk_debug_control = port;
0a7de745 1983 break;
fe8ab488 1984
39037602 1985 default:
f427ee49
A
1986 rc = KERN_INVALID_ARGUMENT;
1987 goto out_unlock;
1c79356b
A
1988 }/* switch */
1989
f427ee49 1990 rc = KERN_SUCCESS;
2d21ac55 1991
f427ee49 1992out_unlock:
1c79356b
A
1993 itk_unlock(task);
1994
0a7de745 1995 if (IP_VALID(old)) {
1c79356b 1996 ipc_port_release_send(old);
0a7de745 1997 }
f427ee49
A
1998out:
1999 return rc;
1c79356b 2000}
1c79356b
A
2001/*
2002 * Routine: mach_ports_register [kernel call]
2003 * Purpose:
2004 * Stash a handful of port send rights in the task.
2005 * Child tasks will inherit these rights, but they
2006 * must use mach_ports_lookup to acquire them.
2007 *
2008 * The rights are supplied in a (wired) kalloc'd segment.
2009 * Rights which aren't supplied are assumed to be null.
2010 * Conditions:
2011 * Nothing locked. If successful, consumes
2012 * the supplied rights and memory.
2013 * Returns:
c3c9b80d
A
2014 * KERN_SUCCESS Stashed the port rights.
2015 * KERN_INVALID_RIGHT Port in array is marked immovable.
1c79356b
A
2016 * KERN_INVALID_ARGUMENT The task is null.
2017 * KERN_INVALID_ARGUMENT The task is dead.
39236c6e 2018 * KERN_INVALID_ARGUMENT The memory param is null.
1c79356b
A
2019 * KERN_INVALID_ARGUMENT Too many port rights supplied.
2020 */
2021
2022kern_return_t
2023mach_ports_register(
0a7de745
A
2024 task_t task,
2025 mach_port_array_t memory,
2026 mach_msg_type_number_t portsCnt)
1c79356b
A
2027{
2028 ipc_port_t ports[TASK_PORT_REGISTER_MAX];
91447636 2029 unsigned int i;
1c79356b
A
2030
2031 if ((task == TASK_NULL) ||
39236c6e 2032 (portsCnt > TASK_PORT_REGISTER_MAX) ||
0a7de745 2033 (portsCnt && memory == NULL)) {
1c79356b 2034 return KERN_INVALID_ARGUMENT;
0a7de745 2035 }
1c79356b
A
2036
2037 /*
2038 * Pad the port rights with nulls.
2039 */
2040
0a7de745 2041 for (i = 0; i < portsCnt; i++) {
1c79356b 2042 ports[i] = memory[i];
c3c9b80d
A
2043 if (IP_VALID(ports[i]) && (ports[i]->ip_immovable_receive || ports[i]->ip_immovable_send)) {
2044 return KERN_INVALID_RIGHT;
2045 }
0a7de745
A
2046 }
2047 for (; i < TASK_PORT_REGISTER_MAX; i++) {
1c79356b 2048 ports[i] = IP_NULL;
0a7de745 2049 }
1c79356b
A
2050
2051 itk_lock(task);
c3c9b80d 2052 if (!task->ipc_active) {
1c79356b
A
2053 itk_unlock(task);
2054 return KERN_INVALID_ARGUMENT;
2055 }
2056
2057 /*
2058 * Replace the old send rights with the new.
2059 * Release the old rights after unlocking.
2060 */
2061
2062 for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
2063 ipc_port_t old;
2064
2065 old = task->itk_registered[i];
2066 task->itk_registered[i] = ports[i];
2067 ports[i] = old;
2068 }
2069
2070 itk_unlock(task);
2071
0a7de745
A
2072 for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
2073 if (IP_VALID(ports[i])) {
1c79356b 2074 ipc_port_release_send(ports[i]);
0a7de745
A
2075 }
2076 }
1c79356b
A
2077
2078 /*
2079 * Now that the operation is known to be successful,
2080 * we can free the memory.
2081 */
2082
0a7de745 2083 if (portsCnt != 0) {
91447636 2084 kfree(memory,
0a7de745
A
2085 (vm_size_t) (portsCnt * sizeof(mach_port_t)));
2086 }
1c79356b
A
2087
2088 return KERN_SUCCESS;
2089}
2090
2091/*
2092 * Routine: mach_ports_lookup [kernel call]
2093 * Purpose:
2094 * Retrieves (clones) the stashed port send rights.
2095 * Conditions:
2096 * Nothing locked. If successful, the caller gets
2097 * rights and memory.
2098 * Returns:
2099 * KERN_SUCCESS Retrieved the send rights.
2100 * KERN_INVALID_ARGUMENT The task is null.
2101 * KERN_INVALID_ARGUMENT The task is dead.
2102 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
2103 */
2104
2105kern_return_t
2106mach_ports_lookup(
0a7de745
A
2107 task_t task,
2108 mach_port_array_t *portsp,
2109 mach_msg_type_number_t *portsCnt)
1c79356b 2110{
91447636 2111 void *memory;
1c79356b
A
2112 vm_size_t size;
2113 ipc_port_t *ports;
2114 int i;
2115
0a7de745 2116 if (task == TASK_NULL) {
1c79356b 2117 return KERN_INVALID_ARGUMENT;
0a7de745 2118 }
1c79356b
A
2119
2120 size = (vm_size_t) (TASK_PORT_REGISTER_MAX * sizeof(ipc_port_t));
2121
2122 memory = kalloc(size);
0a7de745 2123 if (memory == 0) {
1c79356b 2124 return KERN_RESOURCE_SHORTAGE;
0a7de745 2125 }
1c79356b
A
2126
2127 itk_lock(task);
c3c9b80d 2128 if (!task->ipc_active) {
1c79356b
A
2129 itk_unlock(task);
2130
2131 kfree(memory, size);
2132 return KERN_INVALID_ARGUMENT;
2133 }
2134
2135 ports = (ipc_port_t *) memory;
2136
2137 /*
2138 * Clone port rights. Because kalloc'd memory
2139 * is wired, we won't fault while holding the task lock.
2140 */
2141
0a7de745 2142 for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
1c79356b 2143 ports[i] = ipc_port_copy_send(task->itk_registered[i]);
0a7de745 2144 }
1c79356b
A
2145
2146 itk_unlock(task);
2147
2148 *portsp = (mach_port_array_t) ports;
2149 *portsCnt = TASK_PORT_REGISTER_MAX;
2150 return KERN_SUCCESS;
2151}
2152
5ba3f43e
A
2153kern_return_t
2154task_conversion_eval(task_t caller, task_t victim)
2155{
2156 /*
2157 * Tasks are allowed to resolve their own task ports, and the kernel is
2158 * allowed to resolve anyone's task port.
2159 */
2160 if (caller == kernel_task) {
2161 return KERN_SUCCESS;
2162 }
2163
2164 if (caller == victim) {
2165 return KERN_SUCCESS;
2166 }
2167
2168 /*
2169 * Only the kernel can can resolve the kernel's task port. We've established
2170 * by this point that the caller is not kernel_task.
2171 */
d9a64523 2172 if (victim == TASK_NULL || victim == kernel_task) {
5ba3f43e
A
2173 return KERN_INVALID_SECURITY;
2174 }
2175
f427ee49 2176 task_require(victim);
94ff46dc 2177
f427ee49 2178#if !defined(XNU_TARGET_OS_OSX)
5ba3f43e 2179 /*
f427ee49 2180 * On platforms other than macOS, only a platform binary can resolve the task port
5ba3f43e
A
2181 * of another platform binary.
2182 */
2183 if ((victim->t_flags & TF_PLATFORM) && !(caller->t_flags & TF_PLATFORM)) {
2184#if SECURE_KERNEL
2185 return KERN_INVALID_SECURITY;
2186#else
2187 if (cs_relax_platform_task_ports) {
2188 return KERN_SUCCESS;
2189 } else {
2190 return KERN_INVALID_SECURITY;
2191 }
2192#endif /* SECURE_KERNEL */
2193 }
f427ee49 2194#endif /* !defined(XNU_TARGET_OS_OSX) */
5ba3f43e
A
2195
2196 return KERN_SUCCESS;
2197}
2198
1c79356b
A
2199/*
2200 * Routine: convert_port_to_locked_task
2201 * Purpose:
2202 * Internal helper routine to convert from a port to a locked
2203 * task. Used by several routines that try to convert from a
2204 * task port to a reference on some task related object.
2205 * Conditions:
2206 * Nothing locked, blocking OK.
2207 */
c3c9b80d 2208static task_t
f427ee49 2209convert_port_to_locked_task(ipc_port_t port, boolean_t eval)
1c79356b 2210{
5ba3f43e 2211 int try_failed_count = 0;
2d21ac55 2212
1c79356b 2213 while (IP_VALID(port)) {
5ba3f43e 2214 task_t ct = current_task();
1c79356b
A
2215 task_t task;
2216
2217 ip_lock(port);
f427ee49 2218 if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK_CONTROL)) {
1c79356b
A
2219 ip_unlock(port);
2220 return TASK_NULL;
2221 }
ea3f0419 2222 task = (task_t) ip_get_kobject(port);
1c79356b
A
2223 assert(task != TASK_NULL);
2224
f427ee49 2225 if (eval && task_conversion_eval(ct, task)) {
813fb2f6
A
2226 ip_unlock(port);
2227 return TASK_NULL;
2228 }
2229
1c79356b
A
2230 /*
2231 * Normal lock ordering puts task_lock() before ip_lock().
2232 * Attempt out-of-order locking here.
2233 */
2234 if (task_lock_try(task)) {
2235 ip_unlock(port);
0a7de745 2236 return task;
1c79356b 2237 }
2d21ac55 2238 try_failed_count++;
1c79356b
A
2239
2240 ip_unlock(port);
2d21ac55 2241 mutex_pause(try_failed_count);
1c79356b
A
2242 }
2243 return TASK_NULL;
2244}
2245
813fb2f6
A
2246/*
2247 * Routine: convert_port_to_locked_task_inspect
2248 * Purpose:
2249 * Internal helper routine to convert from a port to a locked
2250 * task inspect right. Used by internal routines that try to convert from a
2251 * task inspect port to a reference on some task related object.
2252 * Conditions:
2253 * Nothing locked, blocking OK.
2254 */
c3c9b80d 2255static task_inspect_t
813fb2f6
A
2256convert_port_to_locked_task_inspect(ipc_port_t port)
2257{
0a7de745 2258 int try_failed_count = 0;
813fb2f6
A
2259
2260 while (IP_VALID(port)) {
2261 task_inspect_t task;
2262
2263 ip_lock(port);
f427ee49
A
2264 if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK_CONTROL &&
2265 ip_kotype(port) != IKOT_TASK_READ &&
2266 ip_kotype(port) != IKOT_TASK_INSPECT)) {
813fb2f6
A
2267 ip_unlock(port);
2268 return TASK_INSPECT_NULL;
2269 }
ea3f0419 2270 task = (task_inspect_t) ip_get_kobject(port);
813fb2f6
A
2271 assert(task != TASK_INSPECT_NULL);
2272 /*
2273 * Normal lock ordering puts task_lock() before ip_lock().
2274 * Attempt out-of-order locking here.
2275 */
2276 if (task_lock_try((task_t)task)) {
2277 ip_unlock(port);
2278 return task;
2279 }
2280 try_failed_count++;
2281
2282 ip_unlock(port);
2283 mutex_pause(try_failed_count);
2284 }
2285 return TASK_INSPECT_NULL;
2286}
2287
f427ee49
A
2288/*
2289 * Routine: convert_port_to_locked_task_read
2290 * Purpose:
2291 * Internal helper routine to convert from a port to a locked
2292 * task read right. Used by internal routines that try to convert from a
2293 * task read port to a reference on some task related object.
2294 * Conditions:
2295 * Nothing locked, blocking OK.
2296 */
c3c9b80d
A
2297static task_read_t
2298convert_port_to_locked_task_read(
2299 ipc_port_t port,
2300 boolean_t eval)
f427ee49
A
2301{
2302 int try_failed_count = 0;
2303
2304 while (IP_VALID(port)) {
c3c9b80d 2305 task_t ct = current_task();
f427ee49
A
2306 task_read_t task;
2307
2308 ip_lock(port);
2309 if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK_CONTROL &&
2310 ip_kotype(port) != IKOT_TASK_READ)) {
2311 ip_unlock(port);
2312 return TASK_READ_NULL;
2313 }
c3c9b80d 2314 task = (task_read_t)ipc_kobject_get(port);
f427ee49 2315 assert(task != TASK_READ_NULL);
c3c9b80d
A
2316
2317 if (eval && task_conversion_eval(ct, task)) {
2318 ip_unlock(port);
2319 return TASK_READ_NULL;
2320 }
2321
f427ee49
A
2322 /*
2323 * Normal lock ordering puts task_lock() before ip_lock().
2324 * Attempt out-of-order locking here.
2325 */
2326 if (task_lock_try((task_t)task)) {
2327 ip_unlock(port);
2328 return task;
2329 }
2330 try_failed_count++;
2331
2332 ip_unlock(port);
2333 mutex_pause(try_failed_count);
2334 }
2335 return TASK_READ_NULL;
2336}
2337
cb323159
A
2338static task_t
2339convert_port_to_task_locked(
2340 ipc_port_t port,
f427ee49
A
2341 uint32_t *exec_token,
2342 boolean_t eval)
39037602 2343{
cb323159
A
2344 task_t task = TASK_NULL;
2345
2346 ip_lock_held(port);
2347 require_ip_active(port);
2348
f427ee49 2349 if (ip_kotype(port) == IKOT_TASK_CONTROL) {
ea3f0419 2350 task = (task_t) ip_get_kobject(port);
cb323159
A
2351 assert(task != TASK_NULL);
2352
f427ee49 2353 if (eval && task_conversion_eval(current_task(), task)) {
cb323159
A
2354 return TASK_NULL;
2355 }
2356
2357 if (exec_token) {
2358 *exec_token = task->exec_token;
2359 }
f427ee49 2360
cb323159
A
2361 task_reference_internal(task);
2362 }
2363
2364 return task;
39037602
A
2365}
2366
2367/*
2368 * Routine: convert_port_to_task_with_exec_token
2369 * Purpose:
2370 * Convert from a port to a task and return
2371 * the exec token stored in the task.
2372 * Doesn't consume the port ref; produces a task ref,
2373 * which may be null.
2374 * Conditions:
2375 * Nothing locked.
2376 */
2377task_t
2378convert_port_to_task_with_exec_token(
0a7de745 2379 ipc_port_t port,
f427ee49
A
2380 uint32_t *exec_token,
2381 boolean_t eval)
1c79356b 2382{
0a7de745 2383 task_t task = TASK_NULL;
1c79356b 2384
91447636
A
2385 if (IP_VALID(port)) {
2386 ip_lock(port);
cb323159 2387 if (ip_active(port)) {
f427ee49 2388 task = convert_port_to_task_locked(port, exec_token, eval);
91447636 2389 }
91447636 2390 ip_unlock(port);
1c79356b 2391 }
91447636 2392
0a7de745 2393 return task;
1c79356b
A
2394}
2395
cb323159
A
2396/*
2397 * Routine: convert_port_to_task
2398 * Purpose:
2399 * Convert from a port to a task.
2400 * Doesn't consume the port ref; produces a task ref,
2401 * which may be null.
2402 * Conditions:
2403 * Nothing locked.
2404 */
2405task_t
2406convert_port_to_task(
2407 ipc_port_t port)
2408{
f427ee49 2409 return convert_port_to_task_with_exec_token(port, NULL, TRUE);
cb323159
A
2410}
2411
0c530ab8 2412/*
f427ee49 2413 * Routine: convert_port_to_task_no_eval
0c530ab8 2414 * Purpose:
f427ee49
A
2415 * Convert from a port to a task, skips task_conversion_eval.
2416 * Doesn't consume the port ref; produces a task ref,
0c530ab8
A
2417 * which may be null.
2418 * Conditions:
2419 * Nothing locked.
2420 */
f427ee49
A
2421static task_t
2422convert_port_to_task_no_eval(
0a7de745 2423 ipc_port_t port)
0c530ab8 2424{
f427ee49
A
2425 return convert_port_to_task_with_exec_token(port, NULL, FALSE);
2426}
2427
2428/*
2429 * Routine: convert_port_to_task_name
2430 * Purpose:
2431 * Convert from a port to a task name.
2432 * Doesn't consume the port ref; produces a task name ref,
2433 * which may be null.
2434 * Conditions:
2435 * Nothing locked.
2436 */
2437
2438static task_name_t
2439convert_port_to_task_name_locked(
2440 ipc_port_t port)
2441{
2442 task_name_t task = TASK_NAME_NULL;
2443
2444 ip_lock_held(port);
2445 require_ip_active(port);
2446
2447 if (ip_kotype(port) == IKOT_TASK_CONTROL ||
2448 ip_kotype(port) == IKOT_TASK_READ ||
2449 ip_kotype(port) == IKOT_TASK_INSPECT ||
2450 ip_kotype(port) == IKOT_TASK_NAME) {
2451 task = (task_name_t) ip_get_kobject(port);
2452 assert(task != TASK_NAME_NULL);
2453
2454 task_reference_internal(task);
2455 }
2456
2457 return task;
2458}
2459
2460task_name_t
2461convert_port_to_task_name(
2462 ipc_port_t port)
2463{
2464 task_name_t task = TASK_NULL;
0c530ab8
A
2465
2466 if (IP_VALID(port)) {
2467 ip_lock(port);
f427ee49
A
2468 if (ip_active(port)) {
2469 task = convert_port_to_task_name_locked(port);
2470 }
2471 ip_unlock(port);
2472 }
0c530ab8 2473
f427ee49
A
2474 return task;
2475}
0c530ab8 2476
f427ee49
A
2477/*
2478 * Routine: convert_port_to_task_policy
2479 * Purpose:
2480 * Convert from a port to a task.
2481 * Doesn't consume the port ref; produces a task ref,
2482 * which may be null.
2483 * If the port is being used with task_port_set(), any task port
2484 * type other than TASK_CONTROL requires an entitlement. If the
2485 * port is being used with task_port_get(), TASK_NAME requires an
2486 * entitlement.
2487 * Conditions:
2488 * Nothing locked.
2489 */
2490static task_t
2491convert_port_to_task_policy(ipc_port_t port, boolean_t set)
2492{
2493 task_t task = TASK_NULL;
2494 task_t ctask = current_task();
0c530ab8 2495
f427ee49
A
2496 if (!IP_VALID(port)) {
2497 return TASK_NULL;
2498 }
2499
2500 task = set ?
2501 convert_port_to_task(port) :
2502 convert_port_to_task_inspect(port);
2503
2504 if (task == TASK_NULL &&
2505 IOTaskHasEntitlement(ctask, "com.apple.private.task_policy")) {
2506 task = convert_port_to_task_name(port);
2507 }
2508
2509 if (task_conversion_eval(ctask, task) != KERN_SUCCESS) {
2510 task_deallocate(task);
2511 return TASK_NULL;
0c530ab8
A
2512 }
2513
0a7de745 2514 return task;
0c530ab8
A
2515}
2516
f427ee49
A
2517task_policy_set_t
2518convert_port_to_task_policy_set(ipc_port_t port)
2519{
2520 return convert_port_to_task_policy(port, true);
2521}
2522
2523task_policy_get_t
2524convert_port_to_task_policy_get(ipc_port_t port)
2525{
2526 return convert_port_to_task_policy(port, false);
2527}
2528
cb323159
A
2529static task_inspect_t
2530convert_port_to_task_inspect_locked(
2531 ipc_port_t port)
2532{
2533 task_inspect_t task = TASK_INSPECT_NULL;
2534
2535 ip_lock_held(port);
2536 require_ip_active(port);
2537
f427ee49
A
2538 if (ip_kotype(port) == IKOT_TASK_CONTROL ||
2539 ip_kotype(port) == IKOT_TASK_READ ||
2540 ip_kotype(port) == IKOT_TASK_INSPECT) {
ea3f0419 2541 task = (task_inspect_t) ip_get_kobject(port);
cb323159
A
2542 assert(task != TASK_INSPECT_NULL);
2543
2544 task_reference_internal(task);
2545 }
2546
2547 return task;
2548}
2549
f427ee49
A
2550static task_read_t
2551convert_port_to_task_read_locked(
c3c9b80d
A
2552 ipc_port_t port,
2553 boolean_t eval)
f427ee49
A
2554{
2555 task_read_t task = TASK_READ_NULL;
2556
2557 ip_lock_held(port);
2558 require_ip_active(port);
2559
2560 if (ip_kotype(port) == IKOT_TASK_CONTROL ||
2561 ip_kotype(port) == IKOT_TASK_READ) {
2562 task_t ct = current_task();
c3c9b80d 2563 task = (task_read_t)ipc_kobject_get(port);
f427ee49
A
2564
2565 assert(task != TASK_READ_NULL);
2566
c3c9b80d 2567 if (eval && task_conversion_eval(ct, task)) {
f427ee49
A
2568 return TASK_READ_NULL;
2569 }
2570
2571 task_reference_internal(task);
2572 }
2573
2574 return task;
2575}
2576
2577/*
2578 * Routine: convert_port_to_task_check_type
2579 * Purpose:
2580 * Convert from a port to a task based on port's type.
2581 * Doesn't consume the port ref; produces a task ref,
2582 * which may be null.
2583 * Arguments:
2584 * port: The port that we do conversion on
2585 * kotype: Returns the IKOT_TYPE of the port, if translation succeeded
2586 * at_most: The lowest capability flavor allowed. In mach_task_flavor_t,
2587 * the higher the flavor number, the lesser the capability, hence the name.
2588 * eval_check: Whether to run task_conversion_eval check during the conversion.
2589 * For backward compatibility, some interfaces does not run conversion
2590 * eval on IKOT_TASK_CONTROL.
2591 * Conditions:
2592 * Nothing locked.
2593 * Returns:
2594 * task_t and port's type, if translation succeeded;
2595 * TASK_NULL and IKOT_NONE, if translation failed
2596 */
2597task_t
2598convert_port_to_task_check_type(
2599 ipc_port_t port,
2600 ipc_kobject_type_t *kotype,
2601 mach_task_flavor_t at_most,
2602 boolean_t eval_check)
2603{
2604 task_t task = TASK_NULL;
2605 ipc_kobject_type_t type = IKOT_NONE;
2606
2607 if (!IP_VALID(port) || !ip_active(port)) {
2608 goto out;
2609 }
2610
2611 switch (ip_kotype(port)) {
2612 case IKOT_TASK_CONTROL:
2613 task = eval_check ? convert_port_to_task(port) : convert_port_to_task_no_eval(port);
2614 if (task != TASK_NULL) {
2615 type = IKOT_TASK_CONTROL;
2616 }
2617 break;
2618 case IKOT_TASK_READ:
2619 if (at_most >= TASK_FLAVOR_READ) {
c3c9b80d 2620 task = eval_check ? convert_port_to_task_read(port) : convert_port_to_task_read_no_eval(port);
f427ee49
A
2621 if (task != TASK_READ_NULL) {
2622 type = IKOT_TASK_READ;
2623 }
2624 }
2625 break;
2626 case IKOT_TASK_INSPECT:
2627 if (at_most >= TASK_FLAVOR_INSPECT) {
2628 task = convert_port_to_task_inspect(port);
2629 if (task != TASK_INSPECT_NULL) {
2630 type = IKOT_TASK_INSPECT;
2631 }
2632 }
2633 break;
2634 case IKOT_TASK_NAME:
2635 if (at_most >= TASK_FLAVOR_NAME) {
2636 task = convert_port_to_task_name(port);
2637 if (task != TASK_NAME_NULL) {
2638 type = IKOT_TASK_NAME;
2639 }
2640 }
2641 break;
2642 default:
2643 break;
2644 }
2645
2646out:
2647 if (kotype) {
2648 *kotype = type;
2649 }
2650 return task;
2651}
2652
2653/*
2654 * Routine: convert_port_to_thread_check_type
2655 * Purpose:
2656 * Convert from a port to a thread based on port's type.
2657 * Doesn't consume the port ref; produces a thread ref,
2658 * which may be null.
2659 * This conversion routine is _ONLY_ supposed to be used
2660 * by thread_get_special_port.
2661 * Arguments:
2662 * port: The port that we do conversion on
2663 * kotype: Returns the IKOT_TYPE of the port, if translation succeeded
2664 * at_most: The lowest capability flavor allowed. In mach_thread_flavor_t,
2665 * the higher the flavor number, the lesser the capability, hence the name.
2666 * eval_check: Whether to run task_conversion_eval check during the conversion.
2667 * For backward compatibility, some interfaces do not run
2668 * conversion eval on IKOT_THREAD_CONTROL.
2669 * Conditions:
2670 * Nothing locked.
2671 * Returns:
2672 * thread_t and port's type, if translation succeeded;
2673 * THREAD_NULL and IKOT_NONE, if translation failed
2674 */
2675thread_t
2676convert_port_to_thread_check_type(
2677 ipc_port_t port,
2678 ipc_kobject_type_t *kotype,
2679 mach_thread_flavor_t at_most,
2680 boolean_t eval_check)
2681{
2682 thread_t thread = THREAD_NULL;
2683 ipc_kobject_type_t type = IKOT_NONE;
2684
2685 if (!IP_VALID(port) || !ip_active(port)) {
2686 goto out;
2687 }
2688
2689 switch (ip_kotype(port)) {
2690 case IKOT_THREAD_CONTROL:
2691 thread = eval_check ? convert_port_to_thread(port) : convert_port_to_thread_no_eval(port);
2692 if (thread != THREAD_NULL) {
2693 type = IKOT_THREAD_CONTROL;
2694 }
2695 break;
2696 case IKOT_THREAD_READ:
2697 if (at_most >= THREAD_FLAVOR_READ) {
c3c9b80d 2698 thread = eval_check ? convert_port_to_thread_read(port) : convert_port_to_thread_read_no_eval(port);
f427ee49
A
2699 if (thread != THREAD_READ_NULL) {
2700 type = IKOT_THREAD_READ;
2701 }
2702 }
2703 break;
2704 case IKOT_THREAD_INSPECT:
2705 if (at_most >= THREAD_FLAVOR_INSPECT) {
2706 thread = convert_port_to_thread_inspect(port);
2707 if (thread != THREAD_INSPECT_NULL) {
2708 type = IKOT_THREAD_INSPECT;
2709 }
2710 }
2711 break;
2712 default:
2713 break;
2714 }
2715
2716out:
2717 if (kotype) {
2718 *kotype = type;
2719 }
2720 return thread;
2721}
2722
2723/*
2724 * Routine: convert_port_to_space_check_type
2725 * Purpose:
2726 * Convert from a port to a space based on port's type.
2727 * Doesn't consume the port ref; produces a space ref,
2728 * which may be null.
2729 * Arguments:
2730 * port: The port that we do conversion on
2731 * kotype: Returns the IKOT_TYPE of the port, if translation succeeded
2732 * at_most: The lowest capability flavor allowed. In mach_task_flavor_t,
2733 * the higher the flavor number, the lesser the capability, hence the name.
2734 * eval_check: Whether to run task_conversion_eval check during the conversion.
2735 * For backward compatibility, some interfaces do not run
2736 * conversion eval on IKOT_TASK_CONTROL.
2737 * Conditions:
2738 * Nothing locked.
2739 * Returns:
2740 * ipc_space_t and port's type, if translation succeeded;
2741 * IPC_SPACE_NULL and IKOT_NONE, if translation failed
2742 */
2743ipc_space_t
2744convert_port_to_space_check_type(
2745 ipc_port_t port,
2746 ipc_kobject_type_t *kotype,
2747 mach_task_flavor_t at_most,
2748 boolean_t eval_check)
2749{
2750 ipc_space_t space = IPC_SPACE_NULL;
2751 ipc_kobject_type_t type = IKOT_NONE;
2752
2753 if (!IP_VALID(port) || !ip_active(port)) {
2754 goto out;
2755 }
2756
2757 switch (ip_kotype(port)) {
2758 case IKOT_TASK_CONTROL:
2759 space = eval_check ? convert_port_to_space(port) : convert_port_to_space_no_eval(port);
2760 if (space != IPC_SPACE_NULL) {
2761 type = IKOT_TASK_CONTROL;
2762 }
2763 break;
2764 case IKOT_TASK_READ:
2765 if (at_most >= TASK_FLAVOR_READ) {
c3c9b80d 2766 space = eval_check ? convert_port_to_space_read(port) : convert_port_to_space_read_no_eval(port);
f427ee49
A
2767 if (space != IPC_SPACE_READ_NULL) {
2768 type = IKOT_TASK_READ;
2769 }
2770 }
2771 break;
2772 case IKOT_TASK_INSPECT:
2773 if (at_most >= TASK_FLAVOR_INSPECT) {
2774 space = convert_port_to_space_inspect(port);
2775 if (space != IPC_SPACE_INSPECT_NULL) {
2776 type = IKOT_TASK_INSPECT;
2777 }
2778 }
2779 break;
2780 default:
2781 break;
2782 }
2783
2784out:
2785 if (kotype) {
2786 *kotype = type;
2787 }
2788 return space;
2789}
2790
813fb2f6
A
2791/*
2792 * Routine: convert_port_to_task_inspect
2793 * Purpose:
2794 * Convert from a port to a task inspection right
2795 * Doesn't consume the port ref; produces a task ref,
2796 * which may be null.
2797 * Conditions:
2798 * Nothing locked.
2799 */
2800task_inspect_t
2801convert_port_to_task_inspect(
0a7de745 2802 ipc_port_t port)
813fb2f6
A
2803{
2804 task_inspect_t task = TASK_INSPECT_NULL;
2805
2806 if (IP_VALID(port)) {
2807 ip_lock(port);
cb323159
A
2808 if (ip_active(port)) {
2809 task = convert_port_to_task_inspect_locked(port);
813fb2f6 2810 }
813fb2f6
A
2811 ip_unlock(port);
2812 }
2813
0a7de745 2814 return task;
813fb2f6
A
2815}
2816
f427ee49
A
2817/*
2818 * Routine: convert_port_to_task_read
2819 * Purpose:
2820 * Convert from a port to a task read right
2821 * Doesn't consume the port ref; produces a task ref,
2822 * which may be null.
2823 * Conditions:
2824 * Nothing locked.
2825 */
2826task_read_t
2827convert_port_to_task_read(
2828 ipc_port_t port)
2829{
2830 task_read_t task = TASK_READ_NULL;
2831
2832 if (IP_VALID(port)) {
2833 ip_lock(port);
2834 if (ip_active(port)) {
c3c9b80d
A
2835 task = convert_port_to_task_read_locked(port, TRUE);
2836 }
2837 ip_unlock(port);
2838 }
2839
2840 return task;
2841}
2842
2843static task_read_t
2844convert_port_to_task_read_no_eval(
2845 ipc_port_t port)
2846{
2847 task_read_t task = TASK_READ_NULL;
2848
2849 if (IP_VALID(port)) {
2850 ip_lock(port);
2851 if (ip_active(port)) {
2852 task = convert_port_to_task_read_locked(port, FALSE);
f427ee49
A
2853 }
2854 ip_unlock(port);
2855 }
2856
2857 return task;
2858}
2859
39236c6e
A
2860/*
2861 * Routine: convert_port_to_task_suspension_token
2862 * Purpose:
2863 * Convert from a port to a task suspension token.
2864 * Doesn't consume the port ref; produces a suspension token ref,
2865 * which may be null.
2866 * Conditions:
2867 * Nothing locked.
2868 */
2869task_suspension_token_t
2870convert_port_to_task_suspension_token(
0a7de745 2871 ipc_port_t port)
39236c6e 2872{
0a7de745 2873 task_suspension_token_t task = TASK_NULL;
39236c6e
A
2874
2875 if (IP_VALID(port)) {
2876 ip_lock(port);
2877
0a7de745
A
2878 if (ip_active(port) &&
2879 ip_kotype(port) == IKOT_TASK_RESUME) {
ea3f0419 2880 task = (task_suspension_token_t) ip_get_kobject(port);
39236c6e
A
2881 assert(task != TASK_NULL);
2882
2883 task_reference_internal(task);
2884 }
2885
2886 ip_unlock(port);
2887 }
2888
0a7de745 2889 return task;
39236c6e
A
2890}
2891
1c79356b 2892/*
f427ee49 2893 * Routine: convert_port_to_space_with_flavor
1c79356b
A
2894 * Purpose:
2895 * Convert from a port to a space.
2896 * Doesn't consume the port ref; produces a space ref,
2897 * which may be null.
2898 * Conditions:
2899 * Nothing locked.
2900 */
f427ee49
A
2901static ipc_space_t
2902convert_port_to_space_with_flavor(
2903 ipc_port_t port,
2904 mach_task_flavor_t flavor,
2905 boolean_t eval)
1c79356b
A
2906{
2907 ipc_space_t space;
2908 task_t task;
2909
f427ee49
A
2910 switch (flavor) {
2911 case TASK_FLAVOR_CONTROL:
2912 task = convert_port_to_locked_task(port, eval);
2913 break;
2914 case TASK_FLAVOR_READ:
c3c9b80d 2915 task = convert_port_to_locked_task_read(port, eval);
f427ee49
A
2916 break;
2917 case TASK_FLAVOR_INSPECT:
2918 task = convert_port_to_locked_task_inspect(port);
2919 break;
2920 default:
2921 task = TASK_NULL;
2922 break;
2923 }
1c79356b 2924
0a7de745 2925 if (task == TASK_NULL) {
1c79356b 2926 return IPC_SPACE_NULL;
0a7de745 2927 }
1c79356b
A
2928
2929 if (!task->active) {
2930 task_unlock(task);
2931 return IPC_SPACE_NULL;
2932 }
0a7de745 2933
1c79356b
A
2934 space = task->itk_space;
2935 is_reference(space);
2936 task_unlock(task);
0a7de745 2937 return space;
1c79356b
A
2938}
2939
f427ee49
A
2940ipc_space_t
2941convert_port_to_space(
0a7de745 2942 ipc_port_t port)
813fb2f6 2943{
f427ee49
A
2944 return convert_port_to_space_with_flavor(port, TASK_FLAVOR_CONTROL, TRUE);
2945}
813fb2f6 2946
f427ee49
A
2947static ipc_space_t
2948convert_port_to_space_no_eval(
2949 ipc_port_t port)
2950{
2951 return convert_port_to_space_with_flavor(port, TASK_FLAVOR_CONTROL, FALSE);
2952}
813fb2f6 2953
f427ee49
A
2954ipc_space_read_t
2955convert_port_to_space_read(
2956 ipc_port_t port)
2957{
2958 return convert_port_to_space_with_flavor(port, TASK_FLAVOR_READ, TRUE);
2959}
813fb2f6 2960
c3c9b80d
A
2961static ipc_space_read_t
2962convert_port_to_space_read_no_eval(
2963 ipc_port_t port)
2964{
2965 return convert_port_to_space_with_flavor(port, TASK_FLAVOR_READ, FALSE);
2966}
2967
f427ee49
A
2968ipc_space_inspect_t
2969convert_port_to_space_inspect(
2970 ipc_port_t port)
2971{
2972 return convert_port_to_space_with_flavor(port, TASK_FLAVOR_INSPECT, TRUE);
813fb2f6
A
2973}
2974
1c79356b 2975/*
f427ee49 2976 * Routine: convert_port_to_map_with_flavor
1c79356b
A
2977 * Purpose:
2978 * Convert from a port to a map.
2979 * Doesn't consume the port ref; produces a map ref,
2980 * which may be null.
2981 * Conditions:
2982 * Nothing locked.
2983 */
2984
f427ee49
A
2985static vm_map_t
2986convert_port_to_map_with_flavor(
2987 ipc_port_t port,
2988 mach_task_flavor_t flavor)
1c79356b
A
2989{
2990 task_t task;
2991 vm_map_t map;
2992
f427ee49
A
2993 switch (flavor) {
2994 case TASK_FLAVOR_CONTROL:
c3c9b80d 2995 task = convert_port_to_locked_task(port, TRUE); /* always eval */
f427ee49
A
2996 break;
2997 case TASK_FLAVOR_READ:
c3c9b80d 2998 task = convert_port_to_locked_task_read(port, TRUE); /* always eval */
f427ee49
A
2999 break;
3000 case TASK_FLAVOR_INSPECT:
c3c9b80d 3001 task = convert_port_to_locked_task_inspect(port); /* always no eval */
f427ee49
A
3002 break;
3003 default:
3004 task = TASK_NULL;
3005 break;
3006 }
0a7de745
A
3007
3008 if (task == TASK_NULL) {
1c79356b 3009 return VM_MAP_NULL;
0a7de745 3010 }
1c79356b
A
3011
3012 if (!task->active) {
3013 task_unlock(task);
3014 return VM_MAP_NULL;
3015 }
0a7de745 3016
1c79356b 3017 map = task->map;
f427ee49
A
3018 if (map->pmap == kernel_pmap) {
3019 if (flavor == TASK_FLAVOR_CONTROL) {
3020 panic("userspace has control access to a "
3021 "kernel map %p through task %p", map, task);
3022 }
3023 if (task != kernel_task) {
3024 panic("userspace has access to a "
3025 "kernel map %p through task %p", map, task);
3026 }
3027 } else {
3028 pmap_require(map->pmap);
3029 }
3030
c3c9b80d 3031 vm_map_reference(map);
1c79356b
A
3032 task_unlock(task);
3033 return map;
3034}
3035
f427ee49
A
3036vm_map_read_t
3037convert_port_to_map(
3038 ipc_port_t port)
3039{
3040 return convert_port_to_map_with_flavor(port, TASK_FLAVOR_CONTROL);
3041}
3042
3043vm_map_read_t
3044convert_port_to_map_read(
3045 ipc_port_t port)
3046{
3047 return convert_port_to_map_with_flavor(port, TASK_FLAVOR_READ);
3048}
3049
3050vm_map_inspect_t
3051convert_port_to_map_inspect(
3052 ipc_port_t port)
3053{
3054 return convert_port_to_map_with_flavor(port, TASK_FLAVOR_INSPECT);
3055}
3056
1c79356b
A
3057
3058/*
91447636 3059 * Routine: convert_port_to_thread
1c79356b 3060 * Purpose:
91447636
A
3061 * Convert from a port to a thread.
3062 * Doesn't consume the port ref; produces an thread ref,
1c79356b
A
3063 * which may be null.
3064 * Conditions:
3065 * Nothing locked.
3066 */
3067
cb323159
A
3068static thread_t
3069convert_port_to_thread_locked(
3070 ipc_port_t port,
f427ee49
A
3071 port_to_thread_options_t options,
3072 boolean_t eval)
1c79356b 3073{
0a7de745 3074 thread_t thread = THREAD_NULL;
1c79356b 3075
cb323159
A
3076 ip_lock_held(port);
3077 require_ip_active(port);
1c79356b 3078
f427ee49 3079 if (ip_kotype(port) == IKOT_THREAD_CONTROL) {
ea3f0419 3080 thread = (thread_t) ip_get_kobject(port);
cb323159 3081 assert(thread != THREAD_NULL);
d9a64523 3082
cb323159
A
3083 if (options & PORT_TO_THREAD_NOT_CURRENT_THREAD) {
3084 if (thread == current_thread()) {
3085 return THREAD_NULL;
3086 }
3087 }
3088
3089 if (options & PORT_TO_THREAD_IN_CURRENT_TASK) {
3090 if (thread->task != current_task()) {
3091 return THREAD_NULL;
3092 }
3093 } else {
d9a64523 3094 /* Use task conversion rules for thread control conversions */
f427ee49 3095 if (eval && task_conversion_eval(current_task(), thread->task) != KERN_SUCCESS) {
813fb2f6
A
3096 return THREAD_NULL;
3097 }
1c79356b 3098 }
91447636 3099
cb323159
A
3100 thread_reference_internal(thread);
3101 }
3102
3103 return thread;
3104}
3105
3106thread_t
3107convert_port_to_thread(
3108 ipc_port_t port)
3109{
f427ee49
A
3110 thread_t thread = THREAD_NULL;
3111
3112 if (IP_VALID(port)) {
3113 ip_lock(port);
3114 if (ip_active(port)) {
3115 thread = convert_port_to_thread_locked(port, PORT_TO_THREAD_NONE, TRUE);
3116 }
3117 ip_unlock(port);
3118 }
3119
3120 return thread;
3121}
3122
3123static thread_t
3124convert_port_to_thread_no_eval(
3125 ipc_port_t port)
3126{
3127 thread_t thread = THREAD_NULL;
3128
3129 if (IP_VALID(port)) {
3130 ip_lock(port);
3131 if (ip_active(port)) {
3132 thread = convert_port_to_thread_locked(port, PORT_TO_THREAD_NONE, FALSE);
3133 }
3134 ip_unlock(port);
3135 }
3136
3137 return thread;
3138}
3139
3140/*
3141 * Routine: convert_port_to_thread_inspect
3142 * Purpose:
3143 * Convert from a port to a thread inspect right
3144 * Doesn't consume the port ref; produces a thread ref,
3145 * which may be null.
3146 * Conditions:
3147 * Nothing locked.
3148 */
3149static thread_inspect_t
3150convert_port_to_thread_inspect_locked(
3151 ipc_port_t port)
3152{
3153 thread_inspect_t thread = THREAD_INSPECT_NULL;
3154
3155 ip_lock_held(port);
3156 require_ip_active(port);
3157
3158 if (ip_kotype(port) == IKOT_THREAD_CONTROL ||
3159 ip_kotype(port) == IKOT_THREAD_READ ||
3160 ip_kotype(port) == IKOT_THREAD_INSPECT) {
c3c9b80d 3161 thread = (thread_inspect_t)ipc_kobject_get(port);
f427ee49
A
3162 assert(thread != THREAD_INSPECT_NULL);
3163 thread_reference_internal((thread_t)thread);
3164 }
3165
3166 return thread;
3167}
3168
3169thread_inspect_t
3170convert_port_to_thread_inspect(
3171 ipc_port_t port)
3172{
3173 thread_inspect_t thread = THREAD_INSPECT_NULL;
3174
3175 if (IP_VALID(port)) {
3176 ip_lock(port);
3177 if (ip_active(port)) {
3178 thread = convert_port_to_thread_inspect_locked(port);
3179 }
3180 ip_unlock(port);
3181 }
3182
3183 return thread;
3184}
3185
3186/*
3187 * Routine: convert_port_to_thread_read
3188 * Purpose:
3189 * Convert from a port to a thread read right
3190 * Doesn't consume the port ref; produces a thread ref,
3191 * which may be null.
3192 * Conditions:
3193 * Nothing locked.
3194 */
3195static thread_read_t
3196convert_port_to_thread_read_locked(
c3c9b80d
A
3197 ipc_port_t port,
3198 boolean_t eval)
f427ee49
A
3199{
3200 thread_read_t thread = THREAD_READ_NULL;
3201
3202 ip_lock_held(port);
3203 require_ip_active(port);
3204
3205 if (ip_kotype(port) == IKOT_THREAD_CONTROL ||
3206 ip_kotype(port) == IKOT_THREAD_READ) {
3207 thread = (thread_read_t) ip_get_kobject(port);
3208 assert(thread != THREAD_READ_NULL);
3209
3210 /* Use task conversion rules for thread control conversions */
c3c9b80d 3211 if (eval && task_conversion_eval(current_task(), thread->task) != KERN_SUCCESS) {
f427ee49
A
3212 return THREAD_READ_NULL;
3213 }
3214
3215 thread_reference_internal((thread_t)thread);
3216 }
3217
3218 return thread;
3219}
3220
3221thread_read_t
3222convert_port_to_thread_read(
3223 ipc_port_t port)
3224{
3225 thread_read_t thread = THREAD_READ_NULL;
cb323159
A
3226
3227 if (IP_VALID(port)) {
3228 ip_lock(port);
3229 if (ip_active(port)) {
c3c9b80d
A
3230 thread = convert_port_to_thread_read_locked(port, TRUE);
3231 }
3232 ip_unlock(port);
3233 }
3234
3235 return thread;
3236}
3237
3238static thread_read_t
3239convert_port_to_thread_read_no_eval(
3240 ipc_port_t port)
3241{
3242 thread_read_t thread = THREAD_READ_NULL;
3243
3244 if (IP_VALID(port)) {
3245 ip_lock(port);
3246 if (ip_active(port)) {
3247 thread = convert_port_to_thread_read_locked(port, FALSE);
cb323159 3248 }
91447636 3249 ip_unlock(port);
1c79356b 3250 }
91447636 3251
0a7de745 3252 return thread;
1c79356b
A
3253}
3254
f427ee49 3255
813fb2f6 3256/*
f427ee49 3257 * Routine: convert_thread_to_port_with_flavor
813fb2f6 3258 * Purpose:
f427ee49
A
3259 * Convert from a thread to a port of given flavor.
3260 * Consumes a thread ref; produces a naked send right
3261 * which may be invalid.
813fb2f6
A
3262 * Conditions:
3263 * Nothing locked.
3264 */
f427ee49
A
3265static ipc_port_t
3266convert_thread_to_port_with_flavor(
3267 thread_t thread,
3268 mach_thread_flavor_t flavor)
813fb2f6 3269{
f427ee49 3270 ipc_port_t port = IP_NULL;
813fb2f6 3271
f427ee49 3272 thread_mtx_lock(thread);
813fb2f6 3273
c3c9b80d 3274 if (!thread->ipc_active) {
f427ee49
A
3275 goto exit;
3276 }
3277
3278 if (flavor == THREAD_FLAVOR_CONTROL) {
c3c9b80d 3279 port = ipc_port_make_send(thread->ith_thread_ports[flavor]);
f427ee49 3280 } else {
f427ee49
A
3281 ipc_kobject_type_t kotype = (flavor == THREAD_FLAVOR_READ) ? IKOT_THREAD_READ : IKOT_THREAD_INSPECT;
3282 /*
3283 * Claim a send right on the thread read/inspect port, and request a no-senders
3284 * notification on that port (if none outstanding). A thread reference is not
3285 * donated here even though the ports are created lazily because it doesn't own the
3286 * kobject that it points to. Threads manage their lifetime explicitly and
3287 * have to synchronize with each other, between the task/thread terminating and the
3288 * send-once notification firing, and this is done under the thread mutex
3289 * rather than with atomics.
3290 */
c3c9b80d
A
3291 (void)ipc_kobject_make_send_lazy_alloc_port(&thread->ith_thread_ports[flavor], (ipc_kobject_t)thread,
3292 kotype, IPC_KOBJECT_ALLOC_IMMOVABLE_SEND, false, 0);
3293 port = thread->ith_thread_ports[flavor];
813fb2f6
A
3294 }
3295
f427ee49
A
3296exit:
3297 thread_mtx_unlock(thread);
3298 thread_deallocate(thread);
3299 return port;
813fb2f6
A
3300}
3301
f427ee49
A
3302ipc_port_t
3303convert_thread_to_port(
3304 thread_t thread)
3305{
3306 return convert_thread_to_port_with_flavor(thread, THREAD_FLAVOR_CONTROL);
3307}
3308
3309ipc_port_t
3310convert_thread_read_to_port(thread_read_t thread)
3311{
3312 return convert_thread_to_port_with_flavor(thread, THREAD_FLAVOR_READ);
3313}
813fb2f6
A
3314
3315ipc_port_t
3316convert_thread_inspect_to_port(thread_inspect_t thread)
3317{
f427ee49 3318 return convert_thread_to_port_with_flavor(thread, THREAD_FLAVOR_INSPECT);
813fb2f6
A
3319}
3320
3321
1c79356b 3322/*
91447636 3323 * Routine: port_name_to_thread
1c79356b 3324 * Purpose:
f427ee49 3325 * Convert from a port name to a thread reference
91447636 3326 * A name of MACH_PORT_NULL is valid for the null thread.
1c79356b
A
3327 * Conditions:
3328 * Nothing locked.
3329 */
91447636
A
3330thread_t
3331port_name_to_thread(
cb323159
A
3332 mach_port_name_t name,
3333 port_to_thread_options_t options)
1c79356b 3334{
0a7de745
A
3335 thread_t thread = THREAD_NULL;
3336 ipc_port_t kport;
cb323159 3337 kern_return_t kr;
1c79356b
A
3338
3339 if (MACH_PORT_VALID(name)) {
cb323159
A
3340 kr = ipc_port_translate_send(current_space(), name, &kport);
3341 if (kr == KERN_SUCCESS) {
f427ee49 3342 thread = convert_port_to_thread_locked(kport, options, TRUE);
cb323159 3343 ip_unlock(kport);
0a7de745 3344 }
1c79356b 3345 }
91447636 3346
0a7de745 3347 return thread;
1c79356b
A
3348}
3349
f427ee49
A
3350/*
3351 * Routine: port_name_to_task
3352 * Purpose:
3353 * Convert from a port name to a task reference
3354 * A name of MACH_PORT_NULL is valid for the null task.
3355 * Conditions:
3356 * Nothing locked.
3357 */
1c79356b
A
3358task_t
3359port_name_to_task(
3360 mach_port_name_t name)
3361{
cb323159 3362 ipc_port_t kport;
1c79356b
A
3363 kern_return_t kr;
3364 task_t task = TASK_NULL;
3365
3366 if (MACH_PORT_VALID(name)) {
cb323159
A
3367 kr = ipc_port_translate_send(current_space(), name, &kport);
3368 if (kr == KERN_SUCCESS) {
f427ee49 3369 task = convert_port_to_task_locked(kport, NULL, TRUE);
cb323159 3370 ip_unlock(kport);
0a7de745 3371 }
1c79356b
A
3372 }
3373 return task;
3374}
3375
f427ee49
A
3376/*
3377 * Routine: port_name_to_task_read
3378 * Purpose:
3379 * Convert from a port name to a task reference
3380 * A name of MACH_PORT_NULL is valid for the null task.
3381 * Conditions:
3382 * Nothing locked.
3383 */
3384task_read_t
3385port_name_to_task_read(
3386 mach_port_name_t name)
3387{
3388 ipc_port_t kport;
3389 kern_return_t kr;
3390 task_read_t tr = TASK_READ_NULL;
3391
3392 if (MACH_PORT_VALID(name)) {
3393 kr = ipc_port_translate_send(current_space(), name, &kport);
3394 if (kr == KERN_SUCCESS) {
c3c9b80d 3395 tr = convert_port_to_task_read_locked(kport, TRUE);
f427ee49
A
3396 ip_unlock(kport);
3397 }
3398 }
3399 return tr;
3400}
3401
3402/*
3403 * Routine: port_name_to_task_read_no_eval
3404 * Purpose:
3405 * Convert from a port name to a task reference
3406 * A name of MACH_PORT_NULL is valid for the null task.
c3c9b80d 3407 * Skips task_conversion_eval() during conversion.
f427ee49
A
3408 * Conditions:
3409 * Nothing locked.
3410 */
3411task_read_t
3412port_name_to_task_read_no_eval(
3413 mach_port_name_t name)
3414{
3415 ipc_port_t kport;
3416 kern_return_t kr;
3417 task_read_t tr = TASK_READ_NULL;
3418
3419 if (MACH_PORT_VALID(name)) {
3420 kr = ipc_port_translate_send(current_space(), name, &kport);
3421 if (kr == KERN_SUCCESS) {
c3c9b80d 3422 tr = convert_port_to_task_read_locked(kport, FALSE);
f427ee49
A
3423 ip_unlock(kport);
3424 }
3425 }
3426 return tr;
3427}
3428
f427ee49
A
3429/*
3430 * Routine: port_name_to_task_name
3431 * Purpose:
3432 * Convert from a port name to a task reference
3433 * A name of MACH_PORT_NULL is valid for the null task.
3434 * Conditions:
3435 * Nothing locked.
3436 */
3437task_name_t
3438port_name_to_task_name(
3439 mach_port_name_t name)
3440{
3441 ipc_port_t kport;
3442 kern_return_t kr;
3443 task_name_t tn = TASK_NAME_NULL;
3444
3445 if (MACH_PORT_VALID(name)) {
3446 kr = ipc_port_translate_send(current_space(), name, &kport);
3447 if (kr == KERN_SUCCESS) {
3448 tn = convert_port_to_task_name_locked(kport);
3449 ip_unlock(kport);
3450 }
3451 }
3452 return tn;
3453}
3454
39037602
A
3455/*
3456 * Routine: port_name_to_host
3457 * Purpose:
3458 * Convert from a port name to a host pointer.
3459 * NOTE: This does _not_ return a +1 reference to the host_t
3460 * Conditions:
3461 * Nothing locked.
3462 */
3463host_t
3464port_name_to_host(
3465 mach_port_name_t name)
3466{
39037602
A
3467 host_t host = HOST_NULL;
3468 kern_return_t kr;
3469 ipc_port_t port;
3470
3471 if (MACH_PORT_VALID(name)) {
3472 kr = ipc_port_translate_send(current_space(), name, &port);
3473 if (kr == KERN_SUCCESS) {
3474 host = convert_port_to_host(port);
3475 ip_unlock(port);
3476 }
3477 }
3478 return host;
3479}
3480
1c79356b 3481/*
f427ee49 3482 * Routine: convert_task_to_port_with_flavor
1c79356b 3483 * Purpose:
f427ee49 3484 * Convert from a task to a port of given flavor.
1c79356b 3485 * Consumes a task ref; produces a naked send right
0a7de745 3486 * which may be invalid.
1c79356b
A
3487 * Conditions:
3488 * Nothing locked.
3489 */
f427ee49
A
3490static ipc_port_t
3491convert_task_to_port_with_flavor(
3492 task_t task,
3493 mach_task_flavor_t flavor)
1c79356b 3494{
f427ee49
A
3495 ipc_port_t port = IP_NULL;
3496 ipc_kobject_type_t kotype = IKOT_NONE;
1c79356b
A
3497
3498 itk_lock(task);
813fb2f6 3499
c3c9b80d
A
3500 if (!task->ipc_active) {
3501 goto exit;
3502 }
3503
f427ee49
A
3504 switch (flavor) {
3505 case TASK_FLAVOR_CONTROL:
3506 case TASK_FLAVOR_NAME:
c3c9b80d 3507 port = ipc_port_make_send(task->itk_task_ports[flavor]);
f427ee49
A
3508 break;
3509 /*
3510 * Claim a send right on the task read/inspect port, and request a no-senders
3511 * notification on that port (if none outstanding). A task reference is
3512 * deliberately not donated here because ipc_kobject_make_send_lazy_alloc_port
3513 * is used only for convenience and these ports don't control the lifecycle of
3514 * the task kobject. Instead, the task's itk_lock is used to synchronize the
3515 * handling of the no-senders notification with the task termination.
3516 */
3517 case TASK_FLAVOR_READ:
3518 case TASK_FLAVOR_INSPECT:
f427ee49 3519 kotype = (flavor == TASK_FLAVOR_READ) ? IKOT_TASK_READ : IKOT_TASK_INSPECT;
c3c9b80d
A
3520 (void)ipc_kobject_make_send_lazy_alloc_port((ipc_port_t *) &task->itk_task_ports[flavor],
3521 (ipc_kobject_t)task, kotype, IPC_KOBJECT_ALLOC_IMMOVABLE_SEND, true,
3522 OS_PTRAUTH_DISCRIMINATOR("task.itk_task_ports"));
3523 port = task->itk_task_ports[flavor];
f427ee49
A
3524
3525 break;
0a7de745 3526 }
813fb2f6 3527
f427ee49 3528exit:
1c79356b 3529 itk_unlock(task);
1c79356b
A
3530 task_deallocate(task);
3531 return port;
3532}
3533
f427ee49
A
3534ipc_port_t
3535convert_task_to_port(
3536 task_t task)
3537{
3538 return convert_task_to_port_with_flavor(task, TASK_FLAVOR_CONTROL);
3539}
3540
3541ipc_port_t
3542convert_task_read_to_port(
3543 task_read_t task)
3544{
3545 return convert_task_to_port_with_flavor(task, TASK_FLAVOR_READ);
3546}
3547
813fb2f6
A
3548ipc_port_t
3549convert_task_inspect_to_port(
0a7de745 3550 task_inspect_t task)
813fb2f6 3551{
f427ee49
A
3552 return convert_task_to_port_with_flavor(task, TASK_FLAVOR_INSPECT);
3553}
813fb2f6 3554
f427ee49
A
3555ipc_port_t
3556convert_task_name_to_port(
3557 task_name_t task)
3558{
3559 return convert_task_to_port_with_flavor(task, TASK_FLAVOR_NAME);
813fb2f6
A
3560}
3561
c3c9b80d
A
3562ipc_port_t
3563convert_task_to_port_pinned(
3564 task_t task)
3565{
3566 ipc_port_t port = IP_NULL;
3567
3568 itk_lock(task);
3569
3570 if (task->ipc_active && task->itk_self != IP_NULL) {
3571 port = ipc_port_make_send(task->itk_self);
3572 }
3573
3574 itk_unlock(task);
3575 task_deallocate(task);
3576 return port;
3577}
39236c6e
A
3578/*
3579 * Routine: convert_task_suspend_token_to_port
3580 * Purpose:
3581 * Convert from a task suspension token to a port.
3582 * Consumes a task suspension token ref; produces a naked send-once right
0a7de745 3583 * which may be invalid.
39236c6e
A
3584 * Conditions:
3585 * Nothing locked.
3586 */
3587ipc_port_t
3588convert_task_suspension_token_to_port(
0a7de745 3589 task_suspension_token_t task)
39236c6e
A
3590{
3591 ipc_port_t port;
3592
3593 task_lock(task);
3594 if (task->active) {
3595 if (task->itk_resume == IP_NULL) {
cb323159
A
3596 task->itk_resume = ipc_kobject_alloc_port((ipc_kobject_t) task,
3597 IKOT_TASK_RESUME, IPC_KOBJECT_ALLOC_NONE);
39236c6e
A
3598 }
3599
3600 /*
3601 * Create a send-once right for each instance of a direct user-called
3602 * task_suspend2 call. Each time one of these send-once rights is abandoned,
3603 * the notification handler will resume the target task.
3604 */
3605 port = ipc_port_make_sonce(task->itk_resume);
3606 assert(IP_VALID(port));
3607 } else {
3608 port = IP_NULL;
3609 }
3610
3611 task_unlock(task);
3612 task_suspension_token_deallocate(task);
3613
3614 return port;
3615}
3616
c3c9b80d
A
3617ipc_port_t
3618convert_thread_to_port_pinned(
3619 thread_t thread)
3620{
3621 ipc_port_t port = IP_NULL;
3622
3623 thread_mtx_lock(thread);
3624
3625 if (thread->ipc_active && thread->ith_self != IP_NULL) {
3626 port = ipc_port_make_send(thread->ith_self);
3627 }
3628
3629 thread_mtx_unlock(thread);
3630 thread_deallocate(thread);
3631 return port;
3632}
1c79356b 3633/*
f427ee49 3634 * Routine: space_deallocate
1c79356b 3635 * Purpose:
f427ee49 3636 * Deallocate a space ref produced by convert_port_to_space.
1c79356b
A
3637 * Conditions:
3638 * Nothing locked.
3639 */
3640
f427ee49
A
3641void
3642space_deallocate(
3643 ipc_space_t space)
1c79356b 3644{
f427ee49
A
3645 if (space != IS_NULL) {
3646 is_release(space);
0a7de745 3647 }
1c79356b
A
3648}
3649
3650/*
f427ee49 3651 * Routine: space_read_deallocate
1c79356b 3652 * Purpose:
f427ee49 3653 * Deallocate a space read ref produced by convert_port_to_space_read.
1c79356b
A
3654 * Conditions:
3655 * Nothing locked.
3656 */
3657
3658void
f427ee49
A
3659space_read_deallocate(
3660 ipc_space_read_t space)
1c79356b 3661{
f427ee49
A
3662 if (space != IS_INSPECT_NULL) {
3663 is_release((ipc_space_t)space);
0a7de745 3664 }
1c79356b
A
3665}
3666
813fb2f6
A
3667/*
3668 * Routine: space_inspect_deallocate
3669 * Purpose:
3670 * Deallocate a space inspect ref produced by convert_port_to_space_inspect.
3671 * Conditions:
3672 * Nothing locked.
3673 */
3674
3675void
3676space_inspect_deallocate(
0a7de745 3677 ipc_space_inspect_t space)
813fb2f6 3678{
0a7de745 3679 if (space != IS_INSPECT_NULL) {
813fb2f6 3680 is_release((ipc_space_t)space);
0a7de745 3681 }
813fb2f6
A
3682}
3683
f427ee49 3684
1c79356b
A
3685/*
3686 * Routine: thread/task_set_exception_ports [kernel call]
3687 * Purpose:
3688 * Sets the thread/task exception port, flavor and
3689 * behavior for the exception types specified by the mask.
3690 * There will be one send right per exception per valid
3691 * port.
3692 * Conditions:
3693 * Nothing locked. If successful, consumes
3694 * the supplied send right.
3695 * Returns:
3696 * KERN_SUCCESS Changed the special port.
3697 * KERN_INVALID_ARGUMENT The thread is null,
3698 * Illegal mask bit set.
3699 * Illegal exception behavior
3700 * KERN_FAILURE The thread is dead.
3701 */
3702
3703kern_return_t
3704thread_set_exception_ports(
0a7de745
A
3705 thread_t thread,
3706 exception_mask_t exception_mask,
3707 ipc_port_t new_port,
3708 exception_behavior_t new_behavior,
3709 thread_state_flavor_t new_flavor)
1c79356b 3710{
0a7de745 3711 ipc_port_t old_port[EXC_TYPES_COUNT];
6601e61a 3712 boolean_t privileged = current_task()->sec_token.val[0] == 0;
0a7de745 3713 register int i;
1c79356b 3714
5ba3f43e
A
3715#if CONFIG_MACF
3716 struct label *new_label;
3717#endif
1c79356b 3718
0a7de745
A
3719 if (thread == THREAD_NULL) {
3720 return KERN_INVALID_ARGUMENT;
3721 }
3722
3723 if (exception_mask & ~EXC_MASK_VALID) {
3724 return KERN_INVALID_ARGUMENT;
3725 }
1c79356b
A
3726
3727 if (IP_VALID(new_port)) {
cb323159 3728 switch (new_behavior & ~MACH_EXCEPTION_MASK) {
1c79356b
A
3729 case EXCEPTION_DEFAULT:
3730 case EXCEPTION_STATE:
3731 case EXCEPTION_STATE_IDENTITY:
3732 break;
91447636 3733
1c79356b 3734 default:
0a7de745 3735 return KERN_INVALID_ARGUMENT;
1c79356b
A
3736 }
3737 }
3738
f427ee49 3739
0a7de745 3740 /*
1c79356b
A
3741 * Check the validity of the thread_state_flavor by calling the
3742 * VALID_THREAD_STATE_FLAVOR architecture dependent macro defined in
3743 * osfmk/mach/ARCHITECTURE/thread_status.h
3744 */
0a7de745
A
3745 if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) {
3746 return KERN_INVALID_ARGUMENT;
3747 }
1c79356b 3748
5ba3f43e
A
3749#if CONFIG_MACF
3750 new_label = mac_exc_create_label_for_current_proc();
3751#endif
0a7de745 3752
91447636
A
3753 thread_mtx_lock(thread);
3754
3755 if (!thread->active) {
3756 thread_mtx_unlock(thread);
3757
0a7de745 3758 return KERN_FAILURE;
1c79356b
A
3759 }
3760
39236c6e
A
3761 if (thread->exc_actions == NULL) {
3762 ipc_thread_init_exc_actions(thread);
3763 }
91447636 3764 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) {
39037602
A
3765 if ((exception_mask & (1 << i))
3766#if CONFIG_MACF
0a7de745 3767 && mac_exc_update_action_label(&thread->exc_actions[i], new_label) == 0
39037602 3768#endif
0a7de745 3769 ) {
91447636
A
3770 old_port[i] = thread->exc_actions[i].port;
3771 thread->exc_actions[i].port = ipc_port_copy_send(new_port);
3772 thread->exc_actions[i].behavior = new_behavior;
3773 thread->exc_actions[i].flavor = new_flavor;
6601e61a 3774 thread->exc_actions[i].privileged = privileged;
0a7de745 3775 } else {
1c79356b 3776 old_port[i] = IP_NULL;
0a7de745 3777 }
91447636
A
3778 }
3779
3780 thread_mtx_unlock(thread);
3781
5ba3f43e
A
3782#if CONFIG_MACF
3783 mac_exc_free_label(new_label);
3784#endif
0a7de745
A
3785
3786 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) {
3787 if (IP_VALID(old_port[i])) {
1c79356b 3788 ipc_port_release_send(old_port[i]);
0a7de745
A
3789 }
3790 }
91447636 3791
c3c9b80d 3792 if (IP_VALID(new_port)) { /* consume send right */
1c79356b 3793 ipc_port_release_send(new_port);
0a7de745 3794 }
1c79356b 3795
0a7de745 3796 return KERN_SUCCESS;
91447636 3797}
1c79356b
A
3798
3799kern_return_t
3800task_set_exception_ports(
0a7de745
A
3801 task_t task,
3802 exception_mask_t exception_mask,
3803 ipc_port_t new_port,
3804 exception_behavior_t new_behavior,
3805 thread_state_flavor_t new_flavor)
1c79356b 3806{
0a7de745 3807 ipc_port_t old_port[EXC_TYPES_COUNT];
8ad349bb 3808 boolean_t privileged = current_task()->sec_token.val[0] == 0;
0a7de745 3809 register int i;
1c79356b 3810
5ba3f43e
A
3811#if CONFIG_MACF
3812 struct label *new_label;
0a7de745 3813#endif
5ba3f43e 3814
0a7de745
A
3815 if (task == TASK_NULL) {
3816 return KERN_INVALID_ARGUMENT;
3817 }
1c79356b 3818
0a7de745
A
3819 if (exception_mask & ~EXC_MASK_VALID) {
3820 return KERN_INVALID_ARGUMENT;
3821 }
1c79356b
A
3822
3823 if (IP_VALID(new_port)) {
cb323159 3824 switch (new_behavior & ~MACH_EXCEPTION_MASK) {
1c79356b
A
3825 case EXCEPTION_DEFAULT:
3826 case EXCEPTION_STATE:
3827 case EXCEPTION_STATE_IDENTITY:
3828 break;
91447636 3829
1c79356b 3830 default:
0a7de745 3831 return KERN_INVALID_ARGUMENT;
1c79356b
A
3832 }
3833 }
1c79356b 3834
f427ee49 3835
fe8ab488
A
3836 /*
3837 * Check the validity of the thread_state_flavor by calling the
3838 * VALID_THREAD_STATE_FLAVOR architecture dependent macro defined in
3839 * osfmk/mach/ARCHITECTURE/thread_status.h
3840 */
0a7de745
A
3841 if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) {
3842 return KERN_INVALID_ARGUMENT;
3843 }
fe8ab488 3844
5ba3f43e
A
3845#if CONFIG_MACF
3846 new_label = mac_exc_create_label_for_current_proc();
3847#endif
0a7de745 3848
91447636
A
3849 itk_lock(task);
3850
c3c9b80d 3851 if (!task->ipc_active) {
91447636 3852 itk_unlock(task);
0a7de745 3853 return KERN_FAILURE;
91447636
A
3854 }
3855
3856 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) {
39037602
A
3857 if ((exception_mask & (1 << i))
3858#if CONFIG_MACF
0a7de745 3859 && mac_exc_update_action_label(&task->exc_actions[i], new_label) == 0
39037602 3860#endif
0a7de745 3861 ) {
1c79356b
A
3862 old_port[i] = task->exc_actions[i].port;
3863 task->exc_actions[i].port =
0a7de745 3864 ipc_port_copy_send(new_port);
1c79356b
A
3865 task->exc_actions[i].behavior = new_behavior;
3866 task->exc_actions[i].flavor = new_flavor;
8ad349bb 3867 task->exc_actions[i].privileged = privileged;
0a7de745 3868 } else {
1c79356b 3869 old_port[i] = IP_NULL;
0a7de745 3870 }
91447636 3871 }
1c79356b 3872
91447636
A
3873 itk_unlock(task);
3874
5ba3f43e
A
3875#if CONFIG_MACF
3876 mac_exc_free_label(new_label);
3877#endif
0a7de745
A
3878
3879 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) {
3880 if (IP_VALID(old_port[i])) {
1c79356b 3881 ipc_port_release_send(old_port[i]);
0a7de745
A
3882 }
3883 }
91447636 3884
c3c9b80d 3885 if (IP_VALID(new_port)) { /* consume send right */
1c79356b 3886 ipc_port_release_send(new_port);
0a7de745 3887 }
1c79356b 3888
0a7de745 3889 return KERN_SUCCESS;
91447636 3890}
1c79356b
A
3891
3892/*
3893 * Routine: thread/task_swap_exception_ports [kernel call]
3894 * Purpose:
3895 * Sets the thread/task exception port, flavor and
3896 * behavior for the exception types specified by the
3897 * mask.
3898 *
3899 * The old ports, behavior and flavors are returned
3900 * Count specifies the array sizes on input and
3901 * the number of returned ports etc. on output. The
3902 * arrays must be large enough to hold all the returned
3903 * data, MIG returnes an error otherwise. The masks
3904 * array specifies the corresponding exception type(s).
3905 *
3906 * Conditions:
3907 * Nothing locked. If successful, consumes
3908 * the supplied send right.
3909 *
3910 * Returns upto [in} CountCnt elements.
3911 * Returns:
3912 * KERN_SUCCESS Changed the special port.
3913 * KERN_INVALID_ARGUMENT The thread is null,
3914 * Illegal mask bit set.
3915 * Illegal exception behavior
3916 * KERN_FAILURE The thread is dead.
3917 */
3918
3919kern_return_t
3920thread_swap_exception_ports(
0a7de745
A
3921 thread_t thread,
3922 exception_mask_t exception_mask,
3923 ipc_port_t new_port,
3924 exception_behavior_t new_behavior,
3925 thread_state_flavor_t new_flavor,
3926 exception_mask_array_t masks,
3927 mach_msg_type_number_t *CountCnt,
3928 exception_port_array_t ports,
3929 exception_behavior_array_t behaviors,
3930 thread_state_flavor_array_t flavors)
1c79356b 3931{
0a7de745 3932 ipc_port_t old_port[EXC_TYPES_COUNT];
6601e61a 3933 boolean_t privileged = current_task()->sec_token.val[0] == 0;
0a7de745 3934 unsigned int i, j, count;
1c79356b 3935
5ba3f43e
A
3936#if CONFIG_MACF
3937 struct label *new_label;
3938#endif
3939
0a7de745
A
3940 if (thread == THREAD_NULL) {
3941 return KERN_INVALID_ARGUMENT;
3942 }
1c79356b 3943
0a7de745
A
3944 if (exception_mask & ~EXC_MASK_VALID) {
3945 return KERN_INVALID_ARGUMENT;
3946 }
1c79356b
A
3947
3948 if (IP_VALID(new_port)) {
cb323159 3949 switch (new_behavior & ~MACH_EXCEPTION_MASK) {
1c79356b
A
3950 case EXCEPTION_DEFAULT:
3951 case EXCEPTION_STATE:
3952 case EXCEPTION_STATE_IDENTITY:
3953 break;
91447636 3954
1c79356b 3955 default:
0a7de745 3956 return KERN_INVALID_ARGUMENT;
1c79356b
A
3957 }
3958 }
1c79356b 3959
f427ee49 3960
0a7de745
A
3961 if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) {
3962 return KERN_INVALID_ARGUMENT;
3963 }
fe8ab488 3964
5ba3f43e
A
3965#if CONFIG_MACF
3966 new_label = mac_exc_create_label_for_current_proc();
3967#endif
3968
91447636
A
3969 thread_mtx_lock(thread);
3970
3971 if (!thread->active) {
3972 thread_mtx_unlock(thread);
f427ee49
A
3973#if CONFIG_MACF
3974 mac_exc_free_label(new_label);
3975#endif
0a7de745 3976 return KERN_FAILURE;
1c79356b
A
3977 }
3978
39236c6e
A
3979 if (thread->exc_actions == NULL) {
3980 ipc_thread_init_exc_actions(thread);
3981 }
1c79356b 3982
39236c6e
A
3983 assert(EXC_TYPES_COUNT > FIRST_EXCEPTION);
3984 for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; ++i) {
39037602
A
3985 if ((exception_mask & (1 << i))
3986#if CONFIG_MACF
0a7de745 3987 && mac_exc_update_action_label(&thread->exc_actions[i], new_label) == 0
39037602 3988#endif
0a7de745 3989 ) {
91447636
A
3990 for (j = 0; j < count; ++j) {
3991 /*
3992 * search for an identical entry, if found
3993 * set corresponding mask for this exception.
3994 */
0a7de745
A
3995 if (thread->exc_actions[i].port == ports[j] &&
3996 thread->exc_actions[i].behavior == behaviors[j] &&
3997 thread->exc_actions[i].flavor == flavors[j]) {
1c79356b
A
3998 masks[j] |= (1 << i);
3999 break;
4000 }
91447636
A
4001 }
4002
1c79356b
A
4003 if (j == count) {
4004 masks[j] = (1 << i);
91447636 4005 ports[j] = ipc_port_copy_send(thread->exc_actions[i].port);
1c79356b 4006
91447636
A
4007 behaviors[j] = thread->exc_actions[i].behavior;
4008 flavors[j] = thread->exc_actions[i].flavor;
4009 ++count;
1c79356b
A
4010 }
4011
91447636
A
4012 old_port[i] = thread->exc_actions[i].port;
4013 thread->exc_actions[i].port = ipc_port_copy_send(new_port);
4014 thread->exc_actions[i].behavior = new_behavior;
4015 thread->exc_actions[i].flavor = new_flavor;
6601e61a 4016 thread->exc_actions[i].privileged = privileged;
0a7de745 4017 } else {
1c79356b 4018 old_port[i] = IP_NULL;
0a7de745 4019 }
91447636 4020 }
1c79356b 4021
91447636
A
4022 thread_mtx_unlock(thread);
4023
5ba3f43e
A
4024#if CONFIG_MACF
4025 mac_exc_free_label(new_label);
4026#endif
0a7de745 4027
39236c6e 4028 while (--i >= FIRST_EXCEPTION) {
0a7de745 4029 if (IP_VALID(old_port[i])) {
1c79356b 4030 ipc_port_release_send(old_port[i]);
0a7de745 4031 }
39236c6e 4032 }
91447636 4033
c3c9b80d 4034 if (IP_VALID(new_port)) { /* consume send right */
1c79356b 4035 ipc_port_release_send(new_port);
0a7de745 4036 }
91447636 4037
1c79356b 4038 *CountCnt = count;
91447636 4039
0a7de745 4040 return KERN_SUCCESS;
91447636 4041}
1c79356b
A
4042
4043kern_return_t
4044task_swap_exception_ports(
0a7de745
A
4045 task_t task,
4046 exception_mask_t exception_mask,
4047 ipc_port_t new_port,
4048 exception_behavior_t new_behavior,
4049 thread_state_flavor_t new_flavor,
4050 exception_mask_array_t masks,
4051 mach_msg_type_number_t *CountCnt,
4052 exception_port_array_t ports,
4053 exception_behavior_array_t behaviors,
4054 thread_state_flavor_array_t flavors)
1c79356b 4055{
0a7de745 4056 ipc_port_t old_port[EXC_TYPES_COUNT];
8ad349bb 4057 boolean_t privileged = current_task()->sec_token.val[0] == 0;
0a7de745 4058 unsigned int i, j, count;
1c79356b 4059
5ba3f43e
A
4060#if CONFIG_MACF
4061 struct label *new_label;
0a7de745
A
4062#endif
4063
4064 if (task == TASK_NULL) {
4065 return KERN_INVALID_ARGUMENT;
4066 }
1c79356b 4067
0a7de745
A
4068 if (exception_mask & ~EXC_MASK_VALID) {
4069 return KERN_INVALID_ARGUMENT;
4070 }
1c79356b
A
4071
4072 if (IP_VALID(new_port)) {
cb323159 4073 switch (new_behavior & ~MACH_EXCEPTION_MASK) {
1c79356b
A
4074 case EXCEPTION_DEFAULT:
4075 case EXCEPTION_STATE:
4076 case EXCEPTION_STATE_IDENTITY:
4077 break;
91447636 4078
1c79356b 4079 default:
0a7de745 4080 return KERN_INVALID_ARGUMENT;
1c79356b
A
4081 }
4082 }
1c79356b 4083
f427ee49 4084
0a7de745
A
4085 if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) {
4086 return KERN_INVALID_ARGUMENT;
4087 }
fe8ab488 4088
5ba3f43e
A
4089#if CONFIG_MACF
4090 new_label = mac_exc_create_label_for_current_proc();
4091#endif
0a7de745 4092
1c79356b 4093 itk_lock(task);
91447636 4094
c3c9b80d 4095 if (!task->ipc_active) {
1c79356b 4096 itk_unlock(task);
f427ee49
A
4097#if CONFIG_MACF
4098 mac_exc_free_label(new_label);
4099#endif
0a7de745 4100 return KERN_FAILURE;
1c79356b
A
4101 }
4102
39236c6e
A
4103 assert(EXC_TYPES_COUNT > FIRST_EXCEPTION);
4104 for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; ++i) {
39037602
A
4105 if ((exception_mask & (1 << i))
4106#if CONFIG_MACF
0a7de745 4107 && mac_exc_update_action_label(&task->exc_actions[i], new_label) == 0
39037602 4108#endif
0a7de745 4109 ) {
1c79356b 4110 for (j = 0; j < count; j++) {
91447636
A
4111 /*
4112 * search for an identical entry, if found
4113 * set corresponding mask for this exception.
4114 */
0a7de745
A
4115 if (task->exc_actions[i].port == ports[j] &&
4116 task->exc_actions[i].behavior == behaviors[j] &&
4117 task->exc_actions[i].flavor == flavors[j]) {
1c79356b
A
4118 masks[j] |= (1 << i);
4119 break;
4120 }
91447636
A
4121 }
4122
1c79356b
A
4123 if (j == count) {
4124 masks[j] = (1 << i);
91447636 4125 ports[j] = ipc_port_copy_send(task->exc_actions[i].port);
1c79356b
A
4126 behaviors[j] = task->exc_actions[i].behavior;
4127 flavors[j] = task->exc_actions[i].flavor;
91447636 4128 ++count;
1c79356b 4129 }
91447636 4130
1c79356b 4131 old_port[i] = task->exc_actions[i].port;
39236c6e 4132
0a7de745 4133 task->exc_actions[i].port = ipc_port_copy_send(new_port);
1c79356b
A
4134 task->exc_actions[i].behavior = new_behavior;
4135 task->exc_actions[i].flavor = new_flavor;
8ad349bb 4136 task->exc_actions[i].privileged = privileged;
0a7de745 4137 } else {
1c79356b 4138 old_port[i] = IP_NULL;
0a7de745 4139 }
91447636 4140 }
1c79356b 4141
1c79356b 4142 itk_unlock(task);
91447636 4143
5ba3f43e
A
4144#if CONFIG_MACF
4145 mac_exc_free_label(new_label);
4146#endif
0a7de745 4147
39236c6e 4148 while (--i >= FIRST_EXCEPTION) {
0a7de745 4149 if (IP_VALID(old_port[i])) {
1c79356b 4150 ipc_port_release_send(old_port[i]);
0a7de745 4151 }
39236c6e 4152 }
91447636 4153
c3c9b80d 4154 if (IP_VALID(new_port)) { /* consume send right */
1c79356b 4155 ipc_port_release_send(new_port);
0a7de745 4156 }
91447636 4157
1c79356b
A
4158 *CountCnt = count;
4159
0a7de745 4160 return KERN_SUCCESS;
91447636 4161}
1c79356b
A
4162
4163/*
4164 * Routine: thread/task_get_exception_ports [kernel call]
4165 * Purpose:
4166 * Clones a send right for each of the thread/task's exception
4167 * ports specified in the mask and returns the behaviour
4168 * and flavor of said port.
4169 *
4170 * Returns upto [in} CountCnt elements.
4171 *
4172 * Conditions:
4173 * Nothing locked.
4174 * Returns:
4175 * KERN_SUCCESS Extracted a send right.
4176 * KERN_INVALID_ARGUMENT The thread is null,
4177 * Invalid special port,
4178 * Illegal mask bit set.
4179 * KERN_FAILURE The thread is dead.
4180 */
c3c9b80d
A
4181static kern_return_t
4182thread_get_exception_ports_internal(
4183 thread_t thread,
4184 exception_mask_t exception_mask,
0a7de745
A
4185 exception_mask_array_t masks,
4186 mach_msg_type_number_t *CountCnt,
c3c9b80d 4187 exception_port_info_array_t ports_info,
0a7de745
A
4188 exception_port_array_t ports,
4189 exception_behavior_array_t behaviors,
4190 thread_state_flavor_array_t flavors)
1c79356b 4191{
c3c9b80d
A
4192 unsigned int count;
4193 boolean_t info_only = (ports_info != NULL);
4194 boolean_t dbg_ok = TRUE;
4195 ipc_port_t port_ptrs[EXC_TYPES_COUNT]; /* pointers only, does not hold right */
1c79356b 4196
0a7de745
A
4197 if (thread == THREAD_NULL) {
4198 return KERN_INVALID_ARGUMENT;
4199 }
1c79356b 4200
0a7de745
A
4201 if (exception_mask & ~EXC_MASK_VALID) {
4202 return KERN_INVALID_ARGUMENT;
4203 }
1c79356b 4204
c3c9b80d
A
4205 if (!info_only && !ports) {
4206 return KERN_INVALID_ARGUMENT;
4207 }
4208
4209#if !(DEVELOPMENT || DEBUG) && CONFIG_MACF
4210 if (info_only && mac_task_check_expose_task(kernel_task, TASK_FLAVOR_CONTROL) == 0) {
4211 dbg_ok = TRUE;
4212 } else {
4213 dbg_ok = FALSE;
4214 }
4215#endif
4216
91447636
A
4217 thread_mtx_lock(thread);
4218
4219 if (!thread->active) {
4220 thread_mtx_unlock(thread);
4221
0a7de745 4222 return KERN_FAILURE;
1c79356b
A
4223 }
4224
4225 count = 0;
4226
39236c6e
A
4227 if (thread->exc_actions == NULL) {
4228 goto done;
4229 }
4230
c3c9b80d 4231 for (int i = FIRST_EXCEPTION, j = 0; i < EXC_TYPES_COUNT; ++i) {
1c79356b 4232 if (exception_mask & (1 << i)) {
c3c9b80d
A
4233 ipc_port_t exc_port = thread->exc_actions[i].port;
4234 exception_behavior_t exc_behavior = thread->exc_actions[i].behavior;
4235 thread_state_flavor_t exc_flavor = thread->exc_actions[i].flavor;
4236
91447636
A
4237 for (j = 0; j < count; ++j) {
4238 /*
4239 * search for an identical entry, if found
4240 * set corresponding mask for this exception.
4241 */
c3c9b80d
A
4242 if (exc_port == port_ptrs[j] &&
4243 exc_behavior == behaviors[j] &&
4244 exc_flavor == flavors[j]) {
1c79356b
A
4245 masks[j] |= (1 << i);
4246 break;
4247 }
91447636
A
4248 }
4249
c3c9b80d 4250 if (j == count && count < *CountCnt) {
1c79356b 4251 masks[j] = (1 << i);
c3c9b80d
A
4252 port_ptrs[j] = exc_port;
4253
4254 if (info_only) {
4255 if (!dbg_ok || !IP_VALID(exc_port)) {
4256 /* avoid taking port lock if !dbg_ok */
4257 ports_info[j] = (ipc_info_port_t){ .iip_port_object = 0, .iip_receiver_object = 0 };
4258 } else {
4259 uintptr_t receiver;
4260 (void)ipc_port_get_receiver_task(exc_port, &receiver);
4261 ports_info[j].iip_port_object = (natural_t)VM_KERNEL_ADDRPERM(exc_port);
4262 ports_info[j].iip_receiver_object = receiver ? (natural_t)VM_KERNEL_ADDRPERM(receiver) : 0;
4263 }
4264 } else {
4265 ports[j] = ipc_port_copy_send(exc_port);
0a7de745 4266 }
c3c9b80d
A
4267 behaviors[j] = exc_behavior;
4268 flavors[j] = exc_flavor;
4269 ++count;
1c79356b
A
4270 }
4271 }
91447636 4272 }
1c79356b 4273
39236c6e 4274done:
91447636 4275 thread_mtx_unlock(thread);
1c79356b
A
4276
4277 *CountCnt = count;
91447636 4278
0a7de745 4279 return KERN_SUCCESS;
91447636 4280}
1c79356b 4281
c3c9b80d
A
4282static kern_return_t
4283thread_get_exception_ports(
4284 thread_t thread,
4285 exception_mask_t exception_mask,
4286 exception_mask_array_t masks,
4287 mach_msg_type_number_t *CountCnt,
4288 exception_port_array_t ports,
4289 exception_behavior_array_t behaviors,
4290 thread_state_flavor_array_t flavors)
4291{
4292 return thread_get_exception_ports_internal(thread, exception_mask, masks, CountCnt,
4293 NULL, ports, behaviors, flavors);
4294}
4295
f427ee49 4296kern_return_t
c3c9b80d 4297thread_get_exception_ports_info(
f427ee49
A
4298 mach_port_t port,
4299 exception_mask_t exception_mask,
4300 exception_mask_array_t masks,
c3c9b80d
A
4301 mach_msg_type_number_t *CountCnt,
4302 exception_port_info_array_t ports_info,
f427ee49
A
4303 exception_behavior_array_t behaviors,
4304 thread_state_flavor_array_t flavors)
4305{
4306 kern_return_t kr;
4307
c3c9b80d 4308 thread_t thread = convert_port_to_thread_read_no_eval(port);
f427ee49
A
4309
4310 if (thread == THREAD_NULL) {
4311 return KERN_INVALID_ARGUMENT;
4312 }
4313
c3c9b80d
A
4314 kr = thread_get_exception_ports_internal(thread, exception_mask, masks, CountCnt,
4315 ports_info, NULL, behaviors, flavors);
f427ee49
A
4316
4317 thread_deallocate(thread);
4318 return kr;
4319}
4320
4321kern_return_t
c3c9b80d
A
4322thread_get_exception_ports_from_user(
4323 mach_port_t port,
4324 exception_mask_t exception_mask,
f427ee49 4325 exception_mask_array_t masks,
c3c9b80d 4326 mach_msg_type_number_t *CountCnt,
f427ee49
A
4327 exception_port_array_t ports,
4328 exception_behavior_array_t behaviors,
c3c9b80d
A
4329 thread_state_flavor_array_t flavors)
4330{
4331 kern_return_t kr;
f427ee49 4332
c3c9b80d
A
4333 thread_t thread = convert_port_to_thread_no_eval(port);
4334
4335 if (thread == THREAD_NULL) {
4336 return KERN_INVALID_ARGUMENT;
4337 }
4338
4339 kr = thread_get_exception_ports(thread, exception_mask, masks, CountCnt, ports, behaviors, flavors);
4340
4341 thread_deallocate(thread);
4342 return kr;
4343}
4344
4345static kern_return_t
4346task_get_exception_ports_internal(
4347 task_t task,
4348 exception_mask_t exception_mask,
0a7de745
A
4349 exception_mask_array_t masks,
4350 mach_msg_type_number_t *CountCnt,
c3c9b80d 4351 exception_port_info_array_t ports_info,
0a7de745
A
4352 exception_port_array_t ports,
4353 exception_behavior_array_t behaviors,
4354 thread_state_flavor_array_t flavors)
1c79356b 4355{
c3c9b80d
A
4356 unsigned int count;
4357 boolean_t info_only = (ports_info != NULL);
4358 boolean_t dbg_ok = TRUE;
4359 ipc_port_t port_ptrs[EXC_TYPES_COUNT]; /* pointers only, does not hold right */
1c79356b 4360
0a7de745
A
4361 if (task == TASK_NULL) {
4362 return KERN_INVALID_ARGUMENT;
4363 }
1c79356b 4364
0a7de745
A
4365 if (exception_mask & ~EXC_MASK_VALID) {
4366 return KERN_INVALID_ARGUMENT;
4367 }
1c79356b 4368
c3c9b80d
A
4369 if (!info_only && !ports) {
4370 return KERN_INVALID_ARGUMENT;
4371 }
4372
4373#if !(DEVELOPMENT || DEBUG) && CONFIG_MACF
4374 if (info_only && mac_task_check_expose_task(kernel_task, TASK_FLAVOR_CONTROL) == 0) {
4375 dbg_ok = TRUE;
4376 } else {
4377 dbg_ok = FALSE;
4378 }
4379#endif
4380
1c79356b 4381 itk_lock(task);
91447636 4382
c3c9b80d 4383 if (!task->ipc_active) {
1c79356b 4384 itk_unlock(task);
0a7de745 4385 return KERN_FAILURE;
1c79356b
A
4386 }
4387
4388 count = 0;
4389
c3c9b80d 4390 for (int i = FIRST_EXCEPTION, j = 0; i < EXC_TYPES_COUNT; ++i) {
1c79356b 4391 if (exception_mask & (1 << i)) {
c3c9b80d
A
4392 ipc_port_t exc_port = task->exc_actions[i].port;
4393 exception_behavior_t exc_behavior = task->exc_actions[i].behavior;
4394 thread_state_flavor_t exc_flavor = task->exc_actions[i].flavor;
4395
91447636
A
4396 for (j = 0; j < count; ++j) {
4397 /*
4398 * search for an identical entry, if found
4399 * set corresponding mask for this exception.
4400 */
c3c9b80d
A
4401 if (exc_port == port_ptrs[j] &&
4402 exc_behavior == behaviors[j] &&
4403 exc_flavor == flavors[j]) {
1c79356b
A
4404 masks[j] |= (1 << i);
4405 break;
4406 }
91447636
A
4407 }
4408
c3c9b80d 4409 if (j == count && count < *CountCnt) {
1c79356b 4410 masks[j] = (1 << i);
c3c9b80d
A
4411 port_ptrs[j] = exc_port;
4412
4413 if (info_only) {
4414 if (!dbg_ok || !IP_VALID(exc_port)) {
4415 /* avoid taking port lock if !dbg_ok */
4416 ports_info[j] = (ipc_info_port_t){ .iip_port_object = 0, .iip_receiver_object = 0 };
4417 } else {
4418 uintptr_t receiver;
4419 (void)ipc_port_get_receiver_task(exc_port, &receiver);
4420 ports_info[j].iip_port_object = (natural_t)VM_KERNEL_ADDRPERM(exc_port);
4421 ports_info[j].iip_receiver_object = receiver ? (natural_t)VM_KERNEL_ADDRPERM(receiver) : 0;
4422 }
4423 } else {
4424 ports[j] = ipc_port_copy_send(exc_port);
0a7de745 4425 }
c3c9b80d
A
4426 behaviors[j] = exc_behavior;
4427 flavors[j] = exc_flavor;
4428 ++count;
1c79356b
A
4429 }
4430 }
91447636 4431 }
1c79356b
A
4432
4433 itk_unlock(task);
4434
4435 *CountCnt = count;
91447636 4436
0a7de745 4437 return KERN_SUCCESS;
91447636 4438}
f427ee49 4439
c3c9b80d
A
4440static kern_return_t
4441task_get_exception_ports(
4442 task_t task,
4443 exception_mask_t exception_mask,
4444 exception_mask_array_t masks,
4445 mach_msg_type_number_t *CountCnt,
4446 exception_port_array_t ports,
4447 exception_behavior_array_t behaviors,
4448 thread_state_flavor_array_t flavors)
4449{
4450 return task_get_exception_ports_internal(task, exception_mask, masks, CountCnt,
4451 NULL, ports, behaviors, flavors);
4452}
4453
4454kern_return_t
4455task_get_exception_ports_info(
4456 mach_port_t port,
4457 exception_mask_t exception_mask,
4458 exception_mask_array_t masks,
4459 mach_msg_type_number_t *CountCnt,
4460 exception_port_info_array_t ports_info,
4461 exception_behavior_array_t behaviors,
4462 thread_state_flavor_array_t flavors)
4463{
4464 kern_return_t kr;
4465
4466 task_t task = convert_port_to_task_read_no_eval(port);
4467
4468 if (task == TASK_NULL) {
4469 return KERN_INVALID_ARGUMENT;
4470 }
4471
4472 kr = task_get_exception_ports_internal(task, exception_mask, masks, CountCnt,
4473 ports_info, NULL, behaviors, flavors);
4474
4475 task_deallocate(task);
4476 return kr;
4477}
4478
f427ee49
A
4479kern_return_t
4480task_get_exception_ports_from_user(
4481 mach_port_t port,
4482 exception_mask_t exception_mask,
4483 exception_mask_array_t masks,
4484 mach_msg_type_number_t *CountCnt,
4485 exception_port_array_t ports,
4486 exception_behavior_array_t behaviors,
4487 thread_state_flavor_array_t flavors)
4488{
4489 kern_return_t kr;
4490
c3c9b80d 4491 task_t task = convert_port_to_task_no_eval(port);
f427ee49
A
4492
4493 if (task == TASK_NULL) {
4494 return KERN_INVALID_ARGUMENT;
4495 }
4496
4497 kr = task_get_exception_ports(task, exception_mask, masks, CountCnt, ports, behaviors, flavors);
4498
4499 task_deallocate(task);
4500 return kr;
4501}
c3c9b80d
A
4502
4503/*
4504 * Routine: ipc_thread_port_unpin
4505 * Purpose:
4506 * Called on the thread port when the thread is
4507 * terminating so that the last ref can be deallocated
4508 * without a guard exception.
4509 * Conditions:
4510 * Thread mutex lock is held.
4511 * check_bit should be set to true only when port is expected
4512 * to have ip_pinned bit set.
4513 */
4514void
4515ipc_thread_port_unpin(
4516 ipc_port_t port,
4517 __unused bool check_bit)
4518{
4519 if (port == IP_NULL) {
4520 return;
4521 }
4522 ip_lock(port);
4523 imq_lock(&port->ip_messages);
4524#if DEVELOPMENT || DEBUG
4525 if (pinned_control_port_enabled && check_bit) {
4526 assert(ip_is_control(port)); /*remove once we get rid of boot-arg */
4527 assert(port->ip_pinned == 1);
4528 }
4529#endif
4530 port->ip_pinned = 0;
4531 imq_unlock(&port->ip_messages);
4532 ip_unlock(port);
4533}