1 | /* |
2 | * Copyright (c) 2000-2009 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
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. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
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 |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
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. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | /* |
29 | * @OSF_COPYRIGHT@ |
30 | */ |
31 | /* |
32 | * Mach Operating System |
33 | * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University |
34 | * All Rights Reserved. |
35 | * |
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. |
41 | * |
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. |
45 | * |
46 | * Carnegie Mellon requests users of this software to return to |
47 | * |
48 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
49 | * School of Computer Science |
50 | * Carnegie Mellon University |
51 | * Pittsburgh PA 15213-3890 |
52 | * |
53 | * any improvements or extensions that they make and grant Carnegie Mellon |
54 | * the rights to redistribute these changes. |
55 | */ |
56 | /* |
57 | */ |
58 | |
59 | /* |
60 | * kern/ipc_host.c |
61 | * |
62 | * Routines to implement host ports. |
63 | */ |
64 | #include <mach/message.h> |
65 | #include <mach/mach_traps.h> |
66 | #include <mach/mach_host_server.h> |
67 | #include <mach/host_priv_server.h> |
68 | #include <kern/host.h> |
69 | #include <kern/processor.h> |
70 | #include <kern/task.h> |
71 | #include <kern/thread.h> |
72 | #include <kern/ipc_host.h> |
73 | #include <kern/ipc_kobject.h> |
74 | #include <kern/ux_handler.h> |
75 | #include <kern/misc_protos.h> |
76 | #include <kern/spl.h> |
77 | #include <ipc/ipc_port.h> |
78 | #include <ipc/ipc_space.h> |
79 | |
80 | #if CONFIG_CSR |
81 | #include <sys/csr.h> |
82 | #endif |
83 | |
84 | #if CONFIG_MACF |
85 | #include <security/mac_mach_internal.h> |
86 | #endif |
87 | |
88 | /* |
89 | * ipc_host_init: set up various things. |
90 | */ |
91 | |
92 | extern lck_grp_t host_notify_lock_grp; |
93 | |
94 | IPC_KOBJECT_DEFINE(IKOT_HOST, |
95 | .iko_op_stable = true, |
96 | .iko_op_permanent = true); |
97 | IPC_KOBJECT_DEFINE(IKOT_HOST_PRIV, |
98 | .iko_op_stable = true, |
99 | .iko_op_permanent = true); |
100 | |
101 | IPC_KOBJECT_DEFINE(IKOT_PROCESSOR, |
102 | .iko_op_stable = true, |
103 | .iko_op_permanent = true); |
104 | IPC_KOBJECT_DEFINE(IKOT_PSET, |
105 | .iko_op_stable = true, |
106 | .iko_op_permanent = true); |
107 | IPC_KOBJECT_DEFINE(IKOT_PSET_NAME, |
108 | .iko_op_stable = true, |
109 | .iko_op_permanent = true); |
110 | |
111 | void |
112 | ipc_host_init(void) |
113 | { |
114 | ipc_port_t port; |
115 | int i; |
116 | |
117 | lck_mtx_init(lck: &realhost.lock, grp: &host_notify_lock_grp, LCK_ATTR_NULL); |
118 | |
119 | /* |
120 | * Allocate and set up the two host ports. |
121 | */ |
122 | port = ipc_kobject_alloc_port(kobject: (ipc_kobject_t) &realhost, type: IKOT_HOST, |
123 | options: IPC_KOBJECT_ALLOC_MAKE_SEND); |
124 | kernel_set_special_port(host_priv: &realhost, HOST_PORT, port); |
125 | |
126 | port = ipc_kobject_alloc_port(kobject: (ipc_kobject_t) &realhost, type: IKOT_HOST_PRIV, |
127 | options: IPC_KOBJECT_ALLOC_MAKE_SEND); |
128 | kernel_set_special_port(host_priv: &realhost, HOST_PRIV_PORT, port); |
129 | |
130 | /* the rest of the special ports will be set up later */ |
131 | |
132 | bzero(s: &realhost.exc_actions[0], n: sizeof(realhost.exc_actions[0])); |
133 | for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { |
134 | realhost.exc_actions[i].port = IP_NULL; |
135 | /* The mac framework is not yet initialized, so we defer |
136 | * initializing the labels to later, when they are set |
137 | * for the first time. */ |
138 | realhost.exc_actions[i].label = NULL; |
139 | /* initialize the entire exception action struct */ |
140 | realhost.exc_actions[i].behavior = 0; |
141 | realhost.exc_actions[i].flavor = 0; |
142 | realhost.exc_actions[i].privileged = FALSE; |
143 | } /* for */ |
144 | |
145 | /* |
146 | * Set up ipc for default processor set. |
147 | */ |
148 | ipc_pset_init(pset: &pset0); |
149 | |
150 | /* |
151 | * And for master processor |
152 | */ |
153 | ipc_processor_init(master_processor); |
154 | } |
155 | |
156 | /* |
157 | * Routine: host_self_trap [mach trap] |
158 | * Purpose: |
159 | * Give the caller send rights for his own host port. |
160 | * Conditions: |
161 | * Nothing locked. |
162 | * Returns: |
163 | * MACH_PORT_NULL if there are any resource failures |
164 | * or other errors. |
165 | */ |
166 | |
167 | mach_port_name_t |
168 | host_self_trap( |
169 | __unused struct host_self_trap_args *args) |
170 | { |
171 | task_t self = current_task(); |
172 | ipc_port_t sright; |
173 | mach_port_name_t name; |
174 | |
175 | itk_lock(self); |
176 | sright = host_port_copy_send(port: self->itk_host); |
177 | itk_unlock(self); |
178 | name = ipc_port_copyout_send(sright, current_space()); |
179 | return name; |
180 | } |
181 | |
182 | /* |
183 | * ipc_processor_init: |
184 | * |
185 | * Initialize ipc access to processor by allocating port. |
186 | */ |
187 | |
188 | void |
189 | ipc_processor_init( |
190 | processor_t processor) |
191 | { |
192 | processor->processor_self = ipc_kobject_alloc_port(kobject: processor, |
193 | type: IKOT_PROCESSOR, options: IPC_KOBJECT_ALLOC_NONE); |
194 | } |
195 | |
196 | /* |
197 | * ipc_pset_init: |
198 | * |
199 | * Initialize ipc control of a processor set by allocating its ports. |
200 | */ |
201 | |
202 | void |
203 | ipc_pset_init( |
204 | processor_set_t pset) |
205 | { |
206 | pset->pset_self = ipc_kobject_alloc_port(kobject: pset, |
207 | type: IKOT_PSET, options: IPC_KOBJECT_ALLOC_NONE); |
208 | pset->pset_name_self = ipc_kobject_alloc_port(kobject: pset, |
209 | type: IKOT_PSET_NAME, options: IPC_KOBJECT_ALLOC_NONE); |
210 | } |
211 | |
212 | /* |
213 | * processor_set_default: |
214 | * |
215 | * Return ports for manipulating default_processor set. |
216 | */ |
217 | kern_return_t |
218 | processor_set_default( |
219 | host_t host, |
220 | processor_set_t *pset) |
221 | { |
222 | if (host == HOST_NULL) { |
223 | return KERN_INVALID_ARGUMENT; |
224 | } |
225 | |
226 | *pset = &pset0; |
227 | |
228 | return KERN_SUCCESS; |
229 | } |
230 | |
231 | /* |
232 | * Routine: convert_port_to_host |
233 | * Purpose: |
234 | * Convert from a port to a host. |
235 | * Doesn't consume the port ref; the host produced may be null. |
236 | * Conditions: |
237 | * Nothing locked. |
238 | */ |
239 | |
240 | host_t |
241 | convert_port_to_host( |
242 | ipc_port_t port) |
243 | { |
244 | host_t host = HOST_NULL; |
245 | ipc_kobject_type_t type; |
246 | |
247 | if (IP_VALID(port)) { |
248 | type = ip_kotype(port); |
249 | if (type == IKOT_HOST || type == IKOT_HOST_PRIV) { |
250 | host = (host_t)ipc_kobject_get_stable(port, type); |
251 | if (host && host != &realhost) { |
252 | panic("unexpected host object: %p" , host); |
253 | } |
254 | } |
255 | } |
256 | return host; |
257 | } |
258 | |
259 | /* |
260 | * Routine: convert_port_to_host_priv |
261 | * Purpose: |
262 | * Convert from a port to a host. |
263 | * Doesn't consume the port ref; the host produced may be null. |
264 | * Conditions: |
265 | * Nothing locked. |
266 | */ |
267 | |
268 | host_t |
269 | convert_port_to_host_priv( |
270 | ipc_port_t port) |
271 | { |
272 | host_t host = HOST_NULL; |
273 | |
274 | /* reject translation if itk_host is not host_priv */ |
275 | if (port != current_task()->itk_host) { |
276 | return HOST_NULL; |
277 | } |
278 | |
279 | if (IP_VALID(port)) { |
280 | host = ipc_kobject_get_stable(port, type: IKOT_HOST_PRIV); |
281 | if (host && host != &realhost) { |
282 | panic("unexpected host object: %p" , host); |
283 | } |
284 | } |
285 | |
286 | return host; |
287 | } |
288 | |
289 | /* |
290 | * Routine: convert_port_to_processor |
291 | * Purpose: |
292 | * Convert from a port to a processor. |
293 | * Doesn't consume the port ref; |
294 | * the processor produced may be null. |
295 | * Conditions: |
296 | * Nothing locked. |
297 | */ |
298 | |
299 | processor_t |
300 | convert_port_to_processor( |
301 | ipc_port_t port) |
302 | { |
303 | processor_t processor = PROCESSOR_NULL; |
304 | |
305 | if (IP_VALID(port)) { |
306 | processor = ipc_kobject_get_stable(port, type: IKOT_PROCESSOR); |
307 | } |
308 | |
309 | return processor; |
310 | } |
311 | |
312 | /* |
313 | * Routine: convert_port_to_pset |
314 | * Purpose: |
315 | * Convert from a port to a pset. |
316 | * Doesn't consume the port ref |
317 | * which may be null. |
318 | * Conditions: |
319 | * Nothing locked. |
320 | */ |
321 | |
322 | processor_set_t |
323 | convert_port_to_pset( |
324 | ipc_port_t port) |
325 | { |
326 | processor_set_t pset = PROCESSOR_SET_NULL; |
327 | |
328 | if (IP_VALID(port)) { |
329 | pset = ipc_kobject_get_stable(port, type: IKOT_PSET); |
330 | } |
331 | |
332 | return pset; |
333 | } |
334 | |
335 | /* |
336 | * Routine: convert_port_to_pset_name |
337 | * Purpose: |
338 | * Convert from a port to a pset. |
339 | * Doesn't consume the port ref |
340 | * which may be null. |
341 | * Conditions: |
342 | * Nothing locked. |
343 | */ |
344 | |
345 | processor_set_name_t |
346 | convert_port_to_pset_name( |
347 | ipc_port_t port) |
348 | { |
349 | processor_set_t pset = PROCESSOR_SET_NULL; |
350 | ipc_kobject_type_t type; |
351 | |
352 | if (IP_VALID(port)) { |
353 | type = ip_kotype(port); |
354 | if (type == IKOT_PSET || type == IKOT_PSET_NAME) { |
355 | pset = ipc_kobject_get_stable(port, type); |
356 | } |
357 | } |
358 | return pset; |
359 | } |
360 | |
361 | /* |
362 | * Routine: host_port_copy_send |
363 | * Purpose: |
364 | * Copies a send right for a host port (priv or not) |
365 | * Conditions: |
366 | * Nothing locked. |
367 | */ |
368 | |
369 | ipc_port_t |
370 | host_port_copy_send(ipc_port_t port) |
371 | { |
372 | if (IP_VALID(port)) { |
373 | ipc_kobject_type_t kotype = ip_kotype(port); |
374 | |
375 | if (kotype == IKOT_HOST) { |
376 | port = ipc_kobject_copy_send(port, |
377 | kobject: host_self(), kotype: IKOT_HOST); |
378 | } else if (kotype == IKOT_HOST_PRIV) { |
379 | port = ipc_kobject_copy_send(port, |
380 | kobject: host_priv_self(), kotype: IKOT_HOST_PRIV); |
381 | #if CONFIG_CSR |
382 | } else if (kotype == IKOT_NONE && |
383 | (csr_check(CSR_ALLOW_KERNEL_DEBUGGER) == 0)) { |
384 | port = ipc_port_copy_send_mqueue(port); |
385 | #endif |
386 | } else { |
387 | panic("port %p is an invalid host port" , port); |
388 | } |
389 | } |
390 | |
391 | return port; |
392 | } |
393 | |
394 | /* |
395 | * Routine: convert_host_to_port |
396 | * Purpose: |
397 | * Convert from a host to a port. |
398 | * Produces a naked send right which may be invalid. |
399 | * Conditions: |
400 | * Nothing locked. |
401 | */ |
402 | |
403 | ipc_port_t |
404 | convert_host_to_port( |
405 | host_t host) |
406 | { |
407 | ipc_port_t port = IP_NULL; |
408 | __assert_only kern_return_t kr; |
409 | |
410 | kr = host_get_host_port(host, &port); |
411 | assert(kr == KERN_SUCCESS); |
412 | return port; |
413 | } |
414 | |
415 | /* |
416 | * Routine: convert_processor_to_port |
417 | * Purpose: |
418 | * Convert from a processor to a port. |
419 | * Produces a naked send right which may be invalid. |
420 | * Processors are not reference counted, so nothing to release. |
421 | * Conditions: |
422 | * Nothing locked. |
423 | */ |
424 | |
425 | ipc_port_t |
426 | convert_processor_to_port( |
427 | processor_t processor) |
428 | { |
429 | ipc_port_t port = processor->processor_self; |
430 | |
431 | if (port != IP_NULL) { |
432 | port = ipc_kobject_make_send(port, kobject: processor, kotype: IKOT_PROCESSOR); |
433 | } |
434 | return port; |
435 | } |
436 | |
437 | /* |
438 | * Routine: convert_pset_to_port |
439 | * Purpose: |
440 | * Convert from a pset to a port. |
441 | * Produces a naked send right which may be invalid. |
442 | * Processor sets are not reference counted, so nothing to release. |
443 | * Conditions: |
444 | * Nothing locked. |
445 | */ |
446 | |
447 | ipc_port_t |
448 | convert_pset_to_port( |
449 | processor_set_t pset) |
450 | { |
451 | return ipc_kobject_make_send(port: pset->pset_self, kobject: pset, kotype: IKOT_PSET); |
452 | } |
453 | |
454 | /* |
455 | * Routine: convert_pset_name_to_port |
456 | * Purpose: |
457 | * Convert from a pset to a port. |
458 | * Produces a naked send right which may be invalid. |
459 | * Processor sets are not reference counted, so nothing to release. |
460 | * Conditions: |
461 | * Nothing locked. |
462 | */ |
463 | |
464 | ipc_port_t |
465 | convert_pset_name_to_port( |
466 | processor_set_name_t pset) |
467 | { |
468 | return ipc_kobject_make_send(port: pset->pset_name_self, kobject: pset, kotype: IKOT_PSET_NAME); |
469 | } |
470 | |
471 | /* |
472 | * Routine: host_set_exception_ports [kernel call] |
473 | * Purpose: |
474 | * Sets the host exception port, flavor and |
475 | * behavior for the exception types specified by the mask. |
476 | * There will be one send right per exception per valid |
477 | * port. |
478 | * Conditions: |
479 | * Nothing locked. If successful, consumes |
480 | * the supplied send right. |
481 | * Returns: |
482 | * KERN_SUCCESS Changed the special port. |
483 | * KERN_INVALID_ARGUMENT The host_priv is not valid, |
484 | * Illegal mask bit set. |
485 | * Illegal exception behavior |
486 | * KERN_NO_ACCESS Restricted access to set port |
487 | */ |
488 | kern_return_t |
489 | host_set_exception_ports( |
490 | host_priv_t host_priv, |
491 | exception_mask_t exception_mask, |
492 | ipc_port_t new_port, |
493 | exception_behavior_t new_behavior, |
494 | thread_state_flavor_t new_flavor) |
495 | { |
496 | int i; |
497 | ipc_port_t old_port[EXC_TYPES_COUNT]; |
498 | |
499 | #if CONFIG_MACF |
500 | struct label *deferred_labels[EXC_TYPES_COUNT]; |
501 | struct label *new_label; |
502 | #endif |
503 | |
504 | if (host_priv == HOST_PRIV_NULL) { |
505 | return KERN_INVALID_ARGUMENT; |
506 | } |
507 | |
508 | if (exception_mask & ~EXC_MASK_VALID) { |
509 | return KERN_INVALID_ARGUMENT; |
510 | } |
511 | |
512 | if (IP_VALID(new_port)) { |
513 | switch (new_behavior & ~MACH_EXCEPTION_MASK) { |
514 | case EXCEPTION_DEFAULT: |
515 | case EXCEPTION_STATE: |
516 | case EXCEPTION_STATE_IDENTITY: |
517 | case EXCEPTION_IDENTITY_PROTECTED: |
518 | break; |
519 | default: |
520 | return KERN_INVALID_ARGUMENT; |
521 | } |
522 | } |
523 | |
524 | /* |
525 | * Check the validity of the thread_state_flavor by calling the |
526 | * VALID_THREAD_STATE_FLAVOR architecture dependent macro defined in |
527 | * osfmk/mach/ARCHITECTURE/thread_status.h |
528 | */ |
529 | if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) { |
530 | return KERN_INVALID_ARGUMENT; |
531 | } |
532 | |
533 | if (((new_behavior & ~MACH_EXCEPTION_MASK) == EXCEPTION_IDENTITY_PROTECTED || |
534 | (new_behavior & MACH_EXCEPTION_BACKTRACE_PREFERRED)) |
535 | && !(new_behavior & MACH_EXCEPTION_CODES)) { |
536 | return KERN_INVALID_ARGUMENT; |
537 | } |
538 | |
539 | if (!set_exception_behavior_allowed(new_port, new_behavior, NULL, mask: exception_mask, level: "host" )) { |
540 | return KERN_NO_ACCESS; |
541 | } |
542 | |
543 | #if CONFIG_MACF |
544 | if (mac_task_check_set_host_exception_ports(task: current_task(), exception_mask) != 0) { |
545 | return KERN_NO_ACCESS; |
546 | } |
547 | |
548 | new_label = mac_exc_create_label_for_current_proc(); |
549 | |
550 | for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { |
551 | if (mac_exc_label(action: &host_priv->exc_actions[i]) == NULL) { |
552 | deferred_labels[i] = mac_exc_create_label(action: &host_priv->exc_actions[i]); |
553 | } else { |
554 | deferred_labels[i] = NULL; |
555 | } |
556 | } |
557 | #endif |
558 | |
559 | host_lock(host_priv); |
560 | |
561 | for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { |
562 | #if CONFIG_MACF |
563 | if (mac_exc_label(action: &host_priv->exc_actions[i]) == NULL) { |
564 | // Lazy initialization (see ipc_port_init). |
565 | mac_exc_associate_action_label(action: &host_priv->exc_actions[i], label: deferred_labels[i]); |
566 | deferred_labels[i] = NULL; // Label is used, do not free. |
567 | } |
568 | #endif |
569 | |
570 | if ((exception_mask & (1 << i)) |
571 | #if CONFIG_MACF |
572 | && mac_exc_update_action_label(action: &host_priv->exc_actions[i], newlabel: new_label) == 0 |
573 | #endif |
574 | ) { |
575 | old_port[i] = host_priv->exc_actions[i].port; |
576 | |
577 | host_priv->exc_actions[i].port = |
578 | exception_port_copy_send(port: new_port); |
579 | host_priv->exc_actions[i].behavior = new_behavior; |
580 | host_priv->exc_actions[i].flavor = new_flavor; |
581 | } else { |
582 | old_port[i] = IP_NULL; |
583 | } |
584 | }/* for */ |
585 | |
586 | /* |
587 | * Consume send rights without any lock held. |
588 | */ |
589 | host_unlock(host_priv); |
590 | |
591 | #if CONFIG_MACF |
592 | mac_exc_free_label(label: new_label); |
593 | #endif |
594 | |
595 | for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { |
596 | if (IP_VALID(old_port[i])) { |
597 | ipc_port_release_send(port: old_port[i]); |
598 | } |
599 | #if CONFIG_MACF |
600 | if (deferred_labels[i] != NULL) { |
601 | /* Deferred label went unused: Another thread has completed the lazy initialization. */ |
602 | mac_exc_free_label(label: deferred_labels[i]); |
603 | } |
604 | #endif |
605 | } |
606 | if (IP_VALID(new_port)) { /* consume send right */ |
607 | ipc_port_release_send(port: new_port); |
608 | } |
609 | |
610 | return KERN_SUCCESS; |
611 | } |
612 | |
613 | /* |
614 | * Routine: host_get_exception_ports [kernel call] |
615 | * Purpose: |
616 | * Clones a send right for each of the host's exception |
617 | * ports specified in the mask and returns the behaviour |
618 | * and flavor of said port. |
619 | * |
620 | * Returns upto [in} CountCnt elements. |
621 | * |
622 | * Conditions: |
623 | * Nothing locked. |
624 | * Returns: |
625 | * KERN_SUCCESS Extracted a send right. |
626 | * KERN_INVALID_ARGUMENT Invalid host_priv specified, |
627 | * Invalid special port, |
628 | * Illegal mask bit set. |
629 | * KERN_FAILURE The thread is dead. |
630 | */ |
631 | kern_return_t |
632 | host_get_exception_ports( |
633 | host_priv_t host_priv, |
634 | exception_mask_t exception_mask, |
635 | exception_mask_array_t masks, |
636 | mach_msg_type_number_t * CountCnt, |
637 | exception_port_array_t ports, |
638 | exception_behavior_array_t behaviors, |
639 | thread_state_flavor_array_t flavors ) |
640 | { |
641 | unsigned int i, j, count; |
642 | |
643 | if (host_priv == HOST_PRIV_NULL) { |
644 | return KERN_INVALID_ARGUMENT; |
645 | } |
646 | |
647 | if (exception_mask & ~EXC_MASK_VALID) { |
648 | return KERN_INVALID_ARGUMENT; |
649 | } |
650 | |
651 | host_lock(host_priv); |
652 | |
653 | count = 0; |
654 | |
655 | for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { |
656 | if (exception_mask & (1 << i)) { |
657 | for (j = 0; j < count; j++) { |
658 | /* |
659 | * search for an identical entry, if found |
660 | * set corresponding mask for this exception. |
661 | */ |
662 | if (host_priv->exc_actions[i].port == ports[j] && |
663 | host_priv->exc_actions[i].behavior == behaviors[j] |
664 | && host_priv->exc_actions[i].flavor == flavors[j]) { |
665 | masks[j] |= (1 << i); |
666 | break; |
667 | } |
668 | }/* for */ |
669 | if (j == count && count < *CountCnt) { |
670 | masks[j] = (1 << i); |
671 | ports[j] = |
672 | exception_port_copy_send(port: host_priv->exc_actions[i].port); |
673 | behaviors[j] = host_priv->exc_actions[i].behavior; |
674 | flavors[j] = host_priv->exc_actions[i].flavor; |
675 | count++; |
676 | } |
677 | } |
678 | }/* for */ |
679 | host_unlock(host_priv); |
680 | |
681 | *CountCnt = count; |
682 | return KERN_SUCCESS; |
683 | } |
684 | |
685 | kern_return_t |
686 | host_swap_exception_ports( |
687 | host_priv_t host_priv, |
688 | exception_mask_t exception_mask, |
689 | ipc_port_t new_port, |
690 | exception_behavior_t new_behavior, |
691 | thread_state_flavor_t new_flavor, |
692 | exception_mask_array_t masks, |
693 | mach_msg_type_number_t * CountCnt, |
694 | exception_port_array_t ports, |
695 | exception_behavior_array_t behaviors, |
696 | thread_state_flavor_array_t flavors ) |
697 | { |
698 | unsigned int i, |
699 | j, |
700 | count; |
701 | ipc_port_t old_port[EXC_TYPES_COUNT]; |
702 | |
703 | #if CONFIG_MACF |
704 | struct label *deferred_labels[EXC_TYPES_COUNT]; |
705 | struct label *new_label; |
706 | #endif |
707 | |
708 | if (host_priv == HOST_PRIV_NULL) { |
709 | return KERN_INVALID_ARGUMENT; |
710 | } |
711 | |
712 | if (exception_mask & ~EXC_MASK_VALID) { |
713 | return KERN_INVALID_ARGUMENT; |
714 | } |
715 | |
716 | if (IP_VALID(new_port)) { |
717 | switch (new_behavior & ~MACH_EXCEPTION_MASK) { |
718 | case EXCEPTION_DEFAULT: |
719 | case EXCEPTION_STATE: |
720 | case EXCEPTION_STATE_IDENTITY: |
721 | case EXCEPTION_IDENTITY_PROTECTED: |
722 | break; |
723 | default: |
724 | return KERN_INVALID_ARGUMENT; |
725 | } |
726 | } |
727 | |
728 | if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) { |
729 | return KERN_INVALID_ARGUMENT; |
730 | } |
731 | |
732 | if (((new_behavior & ~MACH_EXCEPTION_MASK) == EXCEPTION_IDENTITY_PROTECTED || |
733 | (new_behavior & MACH_EXCEPTION_BACKTRACE_PREFERRED)) |
734 | && !(new_behavior & MACH_EXCEPTION_CODES)) { |
735 | return KERN_INVALID_ARGUMENT; |
736 | } |
737 | |
738 | if (!set_exception_behavior_allowed(new_port, new_behavior, NULL, mask: exception_mask, level: "host" )) { |
739 | return KERN_NO_ACCESS; |
740 | } |
741 | |
742 | #if CONFIG_MACF |
743 | if (mac_task_check_set_host_exception_ports(task: current_task(), exception_mask) != 0) { |
744 | return KERN_NO_ACCESS; |
745 | } |
746 | |
747 | new_label = mac_exc_create_label_for_current_proc(); |
748 | |
749 | for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { |
750 | if (mac_exc_label(action: &host_priv->exc_actions[i]) == NULL) { |
751 | deferred_labels[i] = mac_exc_create_label(action: &host_priv->exc_actions[i]); |
752 | } else { |
753 | deferred_labels[i] = NULL; |
754 | } |
755 | } |
756 | #endif /* CONFIG_MACF */ |
757 | |
758 | host_lock(host_priv); |
759 | |
760 | assert(EXC_TYPES_COUNT > FIRST_EXCEPTION); |
761 | for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; i++) { |
762 | #if CONFIG_MACF |
763 | if (mac_exc_label(action: &host_priv->exc_actions[i]) == NULL) { |
764 | // Lazy initialization (see ipc_port_init). |
765 | mac_exc_associate_action_label(action: &host_priv->exc_actions[i], label: deferred_labels[i]); |
766 | deferred_labels[i] = NULL; // Label is used, do not free. |
767 | } |
768 | #endif |
769 | |
770 | if ((exception_mask & (1 << i)) |
771 | #if CONFIG_MACF |
772 | && mac_exc_update_action_label(action: &host_priv->exc_actions[i], newlabel: new_label) == 0 |
773 | #endif |
774 | ) { |
775 | for (j = 0; j < count; j++) { |
776 | /* |
777 | * search for an identical entry, if found |
778 | * set corresponding mask for this exception. |
779 | */ |
780 | if (host_priv->exc_actions[i].port == ports[j] && |
781 | host_priv->exc_actions[i].behavior == behaviors[j] |
782 | && host_priv->exc_actions[i].flavor == flavors[j]) { |
783 | masks[j] |= (1 << i); |
784 | break; |
785 | } |
786 | }/* for */ |
787 | if (j == count) { |
788 | masks[j] = (1 << i); |
789 | ports[j] = |
790 | exception_port_copy_send(port: host_priv->exc_actions[i].port); |
791 | behaviors[j] = host_priv->exc_actions[i].behavior; |
792 | flavors[j] = host_priv->exc_actions[i].flavor; |
793 | count++; |
794 | } |
795 | old_port[i] = host_priv->exc_actions[i].port; |
796 | host_priv->exc_actions[i].port = |
797 | exception_port_copy_send(port: new_port); |
798 | host_priv->exc_actions[i].behavior = new_behavior; |
799 | host_priv->exc_actions[i].flavor = new_flavor; |
800 | } else { |
801 | old_port[i] = IP_NULL; |
802 | } |
803 | }/* for */ |
804 | host_unlock(host_priv); |
805 | |
806 | #if CONFIG_MACF |
807 | mac_exc_free_label(label: new_label); |
808 | #endif |
809 | |
810 | /* |
811 | * Consume send rights without any lock held. |
812 | */ |
813 | while (--i >= FIRST_EXCEPTION) { |
814 | if (IP_VALID(old_port[i])) { |
815 | ipc_port_release_send(port: old_port[i]); |
816 | } |
817 | #if CONFIG_MACF |
818 | if (deferred_labels[i] != NULL) { |
819 | mac_exc_free_label(label: deferred_labels[i]); // Label unused. |
820 | } |
821 | #endif |
822 | } |
823 | |
824 | if (IP_VALID(new_port)) { /* consume send right */ |
825 | ipc_port_release_send(port: new_port); |
826 | } |
827 | *CountCnt = count; |
828 | |
829 | return KERN_SUCCESS; |
830 | } |
831 | |