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