]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/exception.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / osfmk / kern / exception.c
1 /*
2 * Copyright (c) 2000-2020 Apple Computer, 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,1987 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 #include <mach/mach_types.h>
60 #include <mach/boolean.h>
61 #include <mach/kern_return.h>
62 #include <mach/message.h>
63 #include <mach/port.h>
64 #include <mach/mig_errors.h>
65 #include <mach/task.h>
66 #include <mach/thread_status.h>
67 #include <mach/exception_types.h>
68 #include <mach/exc.h>
69 #include <mach/mach_exc.h>
70
71 #include <ipc/port.h>
72 #include <ipc/ipc_entry.h>
73 #include <ipc/ipc_object.h>
74 #include <ipc/ipc_notify.h>
75 #include <ipc/ipc_space.h>
76 #include <ipc/ipc_pset.h>
77 #include <ipc/ipc_machdep.h>
78
79 #include <kern/counters.h>
80 #include <kern/ipc_tt.h>
81 #include <kern/task.h>
82 #include <kern/thread.h>
83 #include <kern/processor.h>
84 #include <kern/sched.h>
85 #include <kern/sched_prim.h>
86 #include <kern/host.h>
87 #include <kern/misc_protos.h>
88 #include <kern/ux_handler.h>
89
90 #include <vm/vm_map.h>
91
92 #include <security/mac_mach_internal.h>
93 #include <string.h>
94
95 #include <pexpert/pexpert.h>
96
97 bool panic_on_exception_triage = false;
98
99 unsigned long c_thr_exc_raise = 0;
100 unsigned long c_thr_exc_raise_state = 0;
101 unsigned long c_thr_exc_raise_state_id = 0;
102 unsigned long c_tsk_exc_raise = 0;
103 unsigned long c_tsk_exc_raise_state = 0;
104 unsigned long c_tsk_exc_raise_state_id = 0;
105
106 /* forward declarations */
107 kern_return_t exception_deliver(
108 thread_t thread,
109 exception_type_t exception,
110 mach_exception_data_t code,
111 mach_msg_type_number_t codeCnt,
112 struct exception_action *excp,
113 lck_mtx_t *mutex);
114
115 static kern_return_t
116 check_exc_receiver_dependency(
117 exception_type_t exception,
118 struct exception_action *excp,
119 lck_mtx_t *mutex);
120
121 #ifdef MACH_BSD
122 kern_return_t bsd_exception(
123 exception_type_t exception,
124 mach_exception_data_t code,
125 mach_msg_type_number_t codeCnt);
126 #endif /* MACH_BSD */
127
128 #if __has_feature(ptrauth_calls)
129 extern int exit_with_pac_exception(
130 void *proc,
131 exception_type_t exception,
132 mach_exception_code_t code,
133 mach_exception_subcode_t subcode);
134
135 extern bool proc_is_traced(void *p);
136 #endif /* __has_feature(ptrauth_calls) */
137
138 /*
139 * Routine: exception_init
140 * Purpose:
141 * Global initialization of state for exceptions.
142 * Conditions:
143 * None.
144 */
145 void
146 exception_init(void)
147 {
148 int tmp = 0;
149
150 if (PE_parse_boot_argn("-panic_on_exception_triage", &tmp, sizeof(tmp))) {
151 panic_on_exception_triage = true;
152 }
153 }
154
155 /*
156 * Routine: exception_deliver
157 * Purpose:
158 * Make an upcall to the exception server provided.
159 * Conditions:
160 * Nothing locked and no resources held.
161 * Called from an exception context, so
162 * thread_exception_return and thread_kdb_return
163 * are possible.
164 * Returns:
165 * KERN_SUCCESS if the exception was handled
166 */
167 kern_return_t
168 exception_deliver(
169 thread_t thread,
170 exception_type_t exception,
171 mach_exception_data_t code,
172 mach_msg_type_number_t codeCnt,
173 struct exception_action *excp,
174 lck_mtx_t *mutex)
175 {
176 ipc_port_t exc_port = IPC_PORT_NULL;
177 exception_data_type_t small_code[EXCEPTION_CODE_MAX];
178 int code64;
179 int behavior;
180 int flavor;
181 kern_return_t kr;
182 task_t task;
183 ipc_port_t thread_port = IPC_PORT_NULL, task_port = IPC_PORT_NULL;
184
185 /*
186 * Save work if we are terminating.
187 * Just go back to our AST handler.
188 */
189 if (!thread->active && !thread->inspection) {
190 return KERN_SUCCESS;
191 }
192
193 /*
194 * If there are no exception actions defined for this entity,
195 * we can't deliver here.
196 */
197 if (excp == NULL) {
198 return KERN_FAILURE;
199 }
200
201 assert(exception < EXC_TYPES_COUNT);
202 if (exception >= EXC_TYPES_COUNT) {
203 return KERN_FAILURE;
204 }
205
206 excp = &excp[exception];
207
208 /*
209 * Snapshot the exception action data under lock for consistency.
210 * Hold a reference to the port over the exception_raise_* calls
211 * so it can't be destroyed. This seems like overkill, but keeps
212 * the port from disappearing between now and when
213 * ipc_object_copyin_from_kernel is finally called.
214 */
215 lck_mtx_lock(mutex);
216 exc_port = excp->port;
217 if (!IP_VALID(exc_port)) {
218 lck_mtx_unlock(mutex);
219 return KERN_FAILURE;
220 }
221 ip_lock(exc_port);
222 if (!ip_active(exc_port)) {
223 ip_unlock(exc_port);
224 lck_mtx_unlock(mutex);
225 return KERN_FAILURE;
226 }
227 ip_reference(exc_port);
228 exc_port->ip_srights++;
229 ip_unlock(exc_port);
230
231 flavor = excp->flavor;
232 behavior = excp->behavior;
233 lck_mtx_unlock(mutex);
234
235 code64 = (behavior & MACH_EXCEPTION_CODES);
236 behavior &= ~MACH_EXCEPTION_MASK;
237
238 if (!code64) {
239 small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]);
240 small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]);
241 }
242
243 task = thread->task;
244
245 #if CONFIG_MACF
246 /* Now is a reasonably good time to check if the exception action is
247 * permitted for this process, because after this point we will send
248 * the message out almost certainly.
249 * As with other failures, exception_triage_thread will go on
250 * to the next level.
251 */
252
253 /* The global exception-to-signal translation port is safe to be an exception handler. */
254 if (is_ux_handler_port(exc_port) == FALSE &&
255 mac_exc_action_check_exception_send(task, excp) != 0) {
256 kr = KERN_FAILURE;
257 goto out_release_right;
258 }
259 #endif
260
261 if (behavior != EXCEPTION_STATE) {
262 task_reference(task);
263 task_port = convert_task_to_port(task);
264 /* task ref consumed */
265 thread_reference(thread);
266 thread_port = convert_thread_to_port(thread);
267 /* thread ref consumed */
268 }
269
270 switch (behavior) {
271 case EXCEPTION_STATE: {
272 mach_msg_type_number_t state_cnt;
273 thread_state_data_t state;
274
275 c_thr_exc_raise_state++;
276 state_cnt = _MachineStateCount[flavor];
277 kr = thread_getstatus_to_user(thread, flavor,
278 (thread_state_t)state,
279 &state_cnt);
280 if (kr == KERN_SUCCESS) {
281 if (code64) {
282 kr = mach_exception_raise_state(exc_port,
283 exception,
284 code,
285 codeCnt,
286 &flavor,
287 state, state_cnt,
288 state, &state_cnt);
289 } else {
290 kr = exception_raise_state(exc_port, exception,
291 small_code,
292 codeCnt,
293 &flavor,
294 state, state_cnt,
295 state, &state_cnt);
296 }
297 if (kr == KERN_SUCCESS) {
298 if (exception != EXC_CORPSE_NOTIFY) {
299 kr = thread_setstatus_from_user(thread, flavor,
300 (thread_state_t)state,
301 state_cnt);
302 }
303 goto out_release_right;
304 }
305 }
306
307 goto out_release_right;
308 }
309
310 case EXCEPTION_DEFAULT:
311 c_thr_exc_raise++;
312 if (code64) {
313 kr = mach_exception_raise(exc_port,
314 thread_port,
315 task_port,
316 exception,
317 code,
318 codeCnt);
319 } else {
320 kr = exception_raise(exc_port,
321 thread_port,
322 task_port,
323 exception,
324 small_code,
325 codeCnt);
326 }
327
328 goto out_release_right;
329
330 case EXCEPTION_STATE_IDENTITY: {
331 mach_msg_type_number_t state_cnt;
332 thread_state_data_t state;
333
334 c_thr_exc_raise_state_id++;
335 state_cnt = _MachineStateCount[flavor];
336 kr = thread_getstatus_to_user(thread, flavor,
337 (thread_state_t)state,
338 &state_cnt);
339 if (kr == KERN_SUCCESS) {
340 if (code64) {
341 kr = mach_exception_raise_state_identity(
342 exc_port,
343 thread_port,
344 task_port,
345 exception,
346 code,
347 codeCnt,
348 &flavor,
349 state, state_cnt,
350 state, &state_cnt);
351 } else {
352 kr = exception_raise_state_identity(exc_port,
353 thread_port,
354 task_port,
355 exception,
356 small_code,
357 codeCnt,
358 &flavor,
359 state, state_cnt,
360 state, &state_cnt);
361 }
362
363 if (kr == KERN_SUCCESS) {
364 if (exception != EXC_CORPSE_NOTIFY) {
365 kr = thread_setstatus_from_user(thread, flavor,
366 (thread_state_t)state,
367 state_cnt);
368 }
369 goto out_release_right;
370 }
371 }
372
373 goto out_release_right;
374 }
375
376 default:
377 panic("bad exception behavior!");
378 return KERN_FAILURE;
379 }/* switch */
380
381 out_release_right:
382
383 if (task_port) {
384 ipc_port_release_send(task_port);
385 }
386
387 if (thread_port) {
388 ipc_port_release_send(thread_port);
389 }
390
391 if (exc_port) {
392 ipc_port_release_send(exc_port);
393 }
394
395 return kr;
396 }
397
398 /*
399 * Routine: check_exc_receiver_dependency
400 * Purpose:
401 * Verify that the port destined for receiving this exception is not
402 * on the current task. This would cause hang in kernel for
403 * EXC_CRASH primarily. Note: If port is transferred
404 * between check and delivery then deadlock may happen.
405 *
406 * Conditions:
407 * Nothing locked and no resources held.
408 * Called from an exception context.
409 * Returns:
410 * KERN_SUCCESS if its ok to send exception message.
411 */
412 kern_return_t
413 check_exc_receiver_dependency(
414 exception_type_t exception,
415 struct exception_action *excp,
416 lck_mtx_t *mutex)
417 {
418 kern_return_t retval = KERN_SUCCESS;
419
420 if (excp == NULL || exception != EXC_CRASH) {
421 return retval;
422 }
423
424 task_t task = current_task();
425 lck_mtx_lock(mutex);
426 ipc_port_t xport = excp[exception].port;
427 if (IP_VALID(xport)
428 && ip_active(xport)
429 && task->itk_space == xport->ip_receiver) {
430 retval = KERN_FAILURE;
431 }
432 lck_mtx_unlock(mutex);
433 return retval;
434 }
435
436
437 /*
438 * Routine: exception_triage_thread
439 * Purpose:
440 * The thread caught an exception.
441 * We make an up-call to the thread's exception server.
442 * Conditions:
443 * Nothing locked and no resources held.
444 * Called from an exception context, so
445 * thread_exception_return and thread_kdb_return
446 * are possible.
447 * Returns:
448 * KERN_SUCCESS if exception is handled by any of the handlers.
449 */
450 kern_return_t
451 exception_triage_thread(
452 exception_type_t exception,
453 mach_exception_data_t code,
454 mach_msg_type_number_t codeCnt,
455 thread_t thread)
456 {
457 task_t task;
458 host_priv_t host_priv;
459 lck_mtx_t *mutex;
460 kern_return_t kr = KERN_FAILURE;
461
462
463 assert(exception != EXC_RPC_ALERT);
464
465 /*
466 * If this behavior has been requested by the the kernel
467 * (due to the boot environment), we should panic if we
468 * enter this function. This is intended as a debugging
469 * aid; it should allow us to debug why we caught an
470 * exception in environments where debugging is especially
471 * difficult.
472 */
473 if (panic_on_exception_triage) {
474 panic("called exception_triage when it was forbidden by the boot environment");
475 }
476
477 /*
478 * Try to raise the exception at the activation level.
479 */
480 mutex = &thread->mutex;
481 if (KERN_SUCCESS == check_exc_receiver_dependency(exception, thread->exc_actions, mutex)) {
482 kr = exception_deliver(thread, exception, code, codeCnt, thread->exc_actions, mutex);
483 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
484 goto out;
485 }
486 }
487
488 /*
489 * Maybe the task level will handle it.
490 */
491 task = thread->task;
492 mutex = &task->itk_lock_data;
493 if (KERN_SUCCESS == check_exc_receiver_dependency(exception, task->exc_actions, mutex)) {
494 kr = exception_deliver(thread, exception, code, codeCnt, task->exc_actions, mutex);
495 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
496 goto out;
497 }
498 }
499
500 /*
501 * How about at the host level?
502 */
503 host_priv = host_priv_self();
504 mutex = &host_priv->lock;
505
506 if (KERN_SUCCESS == check_exc_receiver_dependency(exception, host_priv->exc_actions, mutex)) {
507 kr = exception_deliver(thread, exception, code, codeCnt, host_priv->exc_actions, mutex);
508 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
509 goto out;
510 }
511 }
512
513 out:
514 if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) &&
515 (exception != EXC_GUARD) && (exception != EXC_CORPSE_NOTIFY)) {
516 thread_exception_return();
517 }
518 return kr;
519 }
520
521 /*
522 * Routine: exception_triage
523 * Purpose:
524 * The current thread caught an exception.
525 * We make an up-call to the thread's exception server.
526 * Conditions:
527 * Nothing locked and no resources held.
528 * Called from an exception context, so
529 * thread_exception_return and thread_kdb_return
530 * are possible.
531 * Returns:
532 * KERN_SUCCESS if exception is handled by any of the handlers.
533 */
534 int debug4k_panic_on_exception = 0;
535 kern_return_t
536 exception_triage(
537 exception_type_t exception,
538 mach_exception_data_t code,
539 mach_msg_type_number_t codeCnt)
540 {
541 thread_t thread = current_thread();
542 if (VM_MAP_PAGE_SIZE(thread->task->map) < PAGE_SIZE) {
543 DEBUG4K_EXC("thread %p task %p map %p exception %d codes 0x%llx 0x%llx \n", thread, thread->task, thread->task->map, exception, code[0], code[1]);
544 if (debug4k_panic_on_exception) {
545 panic("DEBUG4K %s:%d thread %p task %p map %p exception %d codes 0x%llx 0x%llx \n", __FUNCTION__, __LINE__, thread, thread->task, thread->task->map, exception, code[0], code[1]);
546 }
547 }
548 #if __has_feature(ptrauth_calls)
549 /*
550 * If it is a ptrauth violation, then check if the task has the TF_PAC_EXC_FATAL
551 * flag set and isn't being ptraced. If so, terminate the task via exit_with_reason
552 */
553 if (exception & EXC_PTRAUTH_BIT) {
554 exception &= ~EXC_PTRAUTH_BIT;
555
556 boolean_t traced_flag = FALSE;
557 task_t task = thread->task;
558 void *proc = task->bsd_info;
559
560 if (task->bsd_info) {
561 traced_flag = proc_is_traced(proc);
562 }
563
564 if (task_is_pac_exception_fatal(current_task()) && !traced_flag) {
565 exit_with_pac_exception(proc, exception, code[0], code[1]);
566 thread_exception_return();
567 /* NOT_REACHABLE */
568 }
569 }
570 #endif /* __has_feature(ptrauth_calls) */
571 return exception_triage_thread(exception, code, codeCnt, thread);
572 }
573
574 kern_return_t
575 bsd_exception(
576 exception_type_t exception,
577 mach_exception_data_t code,
578 mach_msg_type_number_t codeCnt)
579 {
580 task_t task;
581 lck_mtx_t *mutex;
582 thread_t self = current_thread();
583 kern_return_t kr;
584
585 /*
586 * Maybe the task level will handle it.
587 */
588 task = current_task();
589 mutex = &task->itk_lock_data;
590
591 kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex);
592
593 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
594 return KERN_SUCCESS;
595 }
596 return KERN_FAILURE;
597 }
598
599
600 /*
601 * Raise an exception on a task.
602 * This should tell launchd to launch Crash Reporter for this task.
603 */
604 kern_return_t
605 task_exception_notify(exception_type_t exception,
606 mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode)
607 {
608 mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
609 wait_interrupt_t wsave;
610 kern_return_t kr = KERN_SUCCESS;
611
612 code[0] = exccode;
613 code[1] = excsubcode;
614
615 wsave = thread_interrupt_level(THREAD_UNINT);
616 kr = exception_triage(exception, code, EXCEPTION_CODE_MAX);
617 (void) thread_interrupt_level(wsave);
618 return kr;
619 }
620
621
622 /*
623 * Handle interface for special performance monitoring
624 * This is a special case of the host exception handler
625 */
626 kern_return_t
627 sys_perf_notify(thread_t thread, int pid)
628 {
629 host_priv_t hostp;
630 ipc_port_t xport;
631 wait_interrupt_t wsave;
632 kern_return_t ret;
633
634 hostp = host_priv_self(); /* Get the host privileged ports */
635 mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
636 code[0] = 0xFF000001; /* Set terminate code */
637 code[1] = pid; /* Pass out the pid */
638
639 struct task *task = thread->task;
640 xport = hostp->exc_actions[EXC_RPC_ALERT].port;
641
642 /* Make sure we're not catching our own exception */
643 if (!IP_VALID(xport) ||
644 !ip_active(xport) ||
645 task->itk_space == xport->data.receiver) {
646 return KERN_FAILURE;
647 }
648
649 wsave = thread_interrupt_level(THREAD_UNINT);
650 ret = exception_deliver(
651 thread,
652 EXC_RPC_ALERT,
653 code,
654 2,
655 hostp->exc_actions,
656 &hostp->lock);
657 (void)thread_interrupt_level(wsave);
658
659 return ret;
660 }