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