osfmk/kern/exception.c

xnu source @ f6217f8 2025-10-16 · 1091 lines · view on GitHub
1/* 2 * Copyright (c) 2000-2024 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_pset.h> 76#include <ipc/ipc_machdep.h> 77 78#include <kern/ipc_tt.h> 79#include <kern/task.h> 80#include <kern/thread.h> 81#include <kern/processor.h> 82#include <kern/sched.h> 83#include <kern/sched_prim.h> 84#include <kern/host.h> 85#include <kern/misc_protos.h> 86#include <kern/ux_handler.h> 87#include <kern/task_ident.h> 88 89#include <vm/vm_map_xnu.h> 90#include <vm/vm_map.h> 91#include <sys/reason.h> 92#include <security/mac_mach_internal.h> 93#include <string.h> 94 95#include <pexpert/pexpert.h> 96 97#include <os/log.h> 98#include <os/system_event_log.h> 99 100#include <libkern/coreanalytics/coreanalytics.h> 101 102#include <sys/code_signing.h> /* for developer mode state */ 103 104bool panic_on_exception_triage = false; 105 106/* Not used in coded, only for inspection during debugging */ 107unsigned long c_thr_exc_raise = 0; 108unsigned long c_thr_exc_raise_identity_token = 0; 109unsigned long c_thr_exc_raise_state_identity_token = 0; 110unsigned long c_thr_exc_raise_state = 0; 111unsigned long c_thr_exc_raise_state_id = 0; 112unsigned long c_thr_exc_raise_backtrace = 0; 113 114/* forward declarations */ 115kern_return_t exception_deliver( 116 thread_t thread, 117 exception_type_t exception, 118 mach_exception_data_t code, 119 mach_msg_type_number_t codeCnt, 120 struct exception_action *excp, 121 lck_mtx_t *mutex); 122 123#ifdef MACH_BSD 124kern_return_t bsd_exception( 125 exception_type_t exception, 126 mach_exception_data_t code, 127 mach_msg_type_number_t codeCnt); 128#endif /* MACH_BSD */ 129 130#ifdef MACH_BSD 131extern bool proc_is_traced(void *p); 132extern int proc_selfpid(void); 133extern char *proc_name_address(struct proc *p); 134#endif /* MACH_BSD */ 135 136#if (DEVELOPMENT || DEBUG) 137TUNABLE_WRITEABLE(unsigned int, exception_log_max_pid, "exception_log_max_pid", 0); 138#endif /* (DEVELOPMENT || DEBUG) */ 139 140/* 141 * Routine: exception_init 142 * Purpose: 143 * Global initialization of state for exceptions. 144 * Conditions: 145 * None. 146 */ 147void 148exception_init(void) 149{ 150 int tmp = 0; 151 152 if (PE_parse_boot_argn("-panic_on_exception_triage", &tmp, sizeof(tmp))) { 153 panic_on_exception_triage = true; 154 } 155 156#if (DEVELOPMENT || DEBUG) 157 if (exception_log_max_pid) { 158 printf("Logging all exceptions where pid < exception_log_max_pid (%d)\n", exception_log_max_pid); 159 } 160#endif /* (DEVELOPMENT || DEBUG) */ 161} 162 163static TUNABLE(bool, pac_replace_ptrs_user, "pac_replace_ptrs_user", true); 164 165ipc_port_t 166exception_port_copy_send(ipc_port_t port) 167{ 168 if (IP_VALID(port)) { 169 if (is_ux_handler_port(port)) { 170 /* is_ux_handler_port() compares against __DATA_CONST */ 171 port = ipc_port_copy_send_any(port); 172 } else { 173 port = ipc_port_copy_send_mqueue(port); 174 } 175 } 176 return port; 177} 178 179/* 180 * Routine: exception_deliver 181 * Purpose: 182 * Make an upcall to the exception server provided. 183 * Conditions: 184 * Nothing locked and no resources held. 185 * Called from an exception context, so 186 * thread_exception_return and thread_kdb_return 187 * are possible. 188 * Returns: 189 * KERN_SUCCESS if the exception was handled 190 */ 191kern_return_t 192exception_deliver( 193 thread_t thread, 194 exception_type_t exception, 195 mach_exception_data_t code, 196 mach_msg_type_number_t codeCnt, 197 struct exception_action *excp, 198 lck_mtx_t *mutex) 199{ 200 ipc_port_t exc_port = IPC_PORT_NULL; 201 exception_data_type_t small_code[EXCEPTION_CODE_MAX]; 202 thread_state_t new_state = NULL; 203 int code64; 204 int behavior; 205 int flavor; 206 kern_return_t kr = KERN_FAILURE; 207 task_t task; 208 task_id_token_t task_token; 209 ipc_port_t thread_port = IPC_PORT_NULL, 210 task_port = IPC_PORT_NULL, 211 task_token_port = IPC_PORT_NULL; 212 thread_set_status_flags_t get_flags = TSSF_TRANSLATE_TO_USER; 213 thread_set_status_flags_t set_flags = TSSF_CHECK_USER_FLAGS; 214 215 /* 216 * Save work if we are terminating. 217 * Just go back to our AST handler. 218 */ 219 if (!thread->active && !thread->inspection) { 220 return KERN_SUCCESS; 221 } 222 223 /* 224 * If there are no exception actions defined for this entity, 225 * we can't deliver here. 226 */ 227 if (excp == NULL) { 228 return KERN_FAILURE; 229 } 230 231 assert(exception < EXC_TYPES_COUNT); 232 if (exception >= EXC_TYPES_COUNT) { 233 return KERN_FAILURE; 234 } 235 236 excp = &excp[exception]; 237 238 /* 239 * Snapshot the exception action data under lock for consistency. 240 * Hold a reference to the port over the exception_raise_* calls 241 * so it can't be destroyed. This seems like overkill, but keeps 242 * the port from disappearing between now and when 243 * ipc_object_copyin_from_kernel is finally called. 244 */ 245 lck_mtx_lock(mutex); 246 exc_port = exception_port_copy_send(excp->port); 247 if (!IP_VALID(exc_port)) { 248 lck_mtx_unlock(mutex); 249 return KERN_FAILURE; 250 } 251 task = get_threadtask(thread); 252 253 flavor = excp->flavor; 254 behavior = excp->behavior; 255 if (excp->hardened) { 256 /* 257 * On arm64e devices we have protected the pc returned via exception 258 * handlers with PAC. We also want to protect all other thread state 259 * for hardened exceptions to prevent modification of any registers 260 * that could affect control flow integrity sometime in the future. 261 */ 262 set_flags |= TSSF_ONLY_PC; 263 } 264 lck_mtx_unlock(mutex); 265 266 code64 = (behavior & MACH_EXCEPTION_CODES); 267 behavior &= ~MACH_EXCEPTION_MASK; 268 269 if (!code64) { 270 small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]); 271 small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]); 272 } 273 274 275#if CONFIG_MACF 276 /* Now is a reasonably good time to check if the exception action is 277 * permitted for this process, because after this point we will send 278 * the message out almost certainly. 279 * As with other failures, exception_triage_thread will go on 280 * to the next level. 281 */ 282 283 /* The global exception-to-signal translation port is safe to be an exception handler. */ 284 if (is_ux_handler_port(exc_port) == FALSE && 285 mac_exc_action_check_exception_send(task, excp) != 0) { 286 kr = KERN_FAILURE; 287 goto out_release_right; 288 } 289#endif 290 291 thread->options |= TH_IN_MACH_EXCEPTION; 292 293 switch (behavior) { 294 case EXCEPTION_STATE: { 295 mach_msg_type_number_t old_state_cnt, new_state_cnt; 296 thread_state_data_t old_state; 297 bool task_allow_user_state = task_needs_user_signed_thread_state(task); 298 299 if (pac_replace_ptrs_user || task_allow_user_state) { 300 get_flags |= TSSF_RANDOM_USER_DIV; 301 set_flags |= (TSSF_ALLOW_ONLY_USER_PTRS | TSSF_RANDOM_USER_DIV); 302 } 303 304 c_thr_exc_raise_state++; 305 assert(flavor < THREAD_STATE_FLAVORS); 306 old_state_cnt = (flavor < THREAD_STATE_FLAVORS) ? _MachineStateCount[flavor] : 0; 307 kr = thread_getstatus_to_user(thread, flavor, 308 (thread_state_t)old_state, 309 &old_state_cnt, get_flags); 310 new_state_cnt = old_state_cnt; 311 if (kr == KERN_SUCCESS) { 312 new_state = (thread_state_t)kalloc_data(sizeof(thread_state_data_t), Z_WAITOK | Z_ZERO); 313 if (new_state == NULL) { 314 kr = KERN_RESOURCE_SHORTAGE; 315 goto out_release_right; 316 } 317 if (code64) { 318 kr = mach_exception_raise_state(exc_port, 319 exception, 320 code, 321 codeCnt, 322 &flavor, 323 old_state, old_state_cnt, 324 new_state, &new_state_cnt); 325 } else { 326 kr = exception_raise_state(exc_port, exception, 327 small_code, 328 codeCnt, 329 &flavor, 330 old_state, old_state_cnt, 331 new_state, &new_state_cnt); 332 } 333 if (kr == KERN_SUCCESS) { 334 if (exception != EXC_CORPSE_NOTIFY) { 335 kr = thread_setstatus_from_user(thread, flavor, 336 (thread_state_t)new_state, new_state_cnt, 337 (thread_state_t)old_state, old_state_cnt, 338 set_flags); 339 } 340 goto out_release_right; 341 } 342 } 343 344 goto out_release_right; 345 } 346 347 case EXCEPTION_DEFAULT: { 348 c_thr_exc_raise++; 349 350 task_reference(task); 351 thread_reference(thread); 352 /* 353 * Only deliver control port if Developer Mode enabled, 354 * or task is a corpse. Otherwise we only deliver the 355 * (immovable) read port in exception handler (both in 356 * or out of process). (94669540) 357 */ 358 if (developer_mode_state() || task_is_a_corpse(task)) { 359 task_port = convert_task_to_port(task); 360 thread_port = convert_thread_to_port(thread); 361 } else { 362 task_port = convert_task_read_to_port(task); 363 thread_port = convert_thread_read_to_port(thread); 364 } 365 /* task and thread ref consumed */ 366 367 if (code64) { 368 kr = mach_exception_raise(exc_port, 369 thread_port, 370 task_port, 371 exception, 372 code, 373 codeCnt); 374 } else { 375 kr = exception_raise(exc_port, 376 thread_port, 377 task_port, 378 exception, 379 small_code, 380 codeCnt); 381 } 382 383 goto out_release_right; 384 } 385 386 case EXCEPTION_IDENTITY_PROTECTED: { 387 c_thr_exc_raise_identity_token++; 388 389 kr = task_create_identity_token(task, &task_token); 390 if (!task->active && kr == KERN_INVALID_ARGUMENT) { 391 /* The task is terminating, don't need to send more exceptions */ 392 kr = KERN_SUCCESS; 393 goto out_release_right; 394 } 395 /* task_token now represents a task, or corpse */ 396 assert(kr == KERN_SUCCESS); 397 task_token_port = convert_task_id_token_to_port(task_token); 398 /* task token ref consumed */ 399 400 if (code64) { 401 kr = mach_exception_raise_identity_protected(exc_port, 402 thread->thread_id, 403 task_token_port, 404 exception, 405 code, 406 codeCnt); 407 } else { 408 panic("mach_exception_raise_identity_protected() must be code64"); 409 } 410 411 goto out_release_right; 412 } 413 414 415 case EXCEPTION_STATE_IDENTITY_PROTECTED: { 416 mach_msg_type_number_t old_state_cnt, new_state_cnt; 417 thread_state_data_t old_state; 418 bool task_allow_user_state = task_needs_user_signed_thread_state(task); 419 420 if (pac_replace_ptrs_user || task_allow_user_state) { 421 set_flags |= TSSF_ALLOW_ONLY_USER_PTRS; 422 if (excp->hardened) { 423 /* Use the signed_pc_key diversifier on the task for authentication. */ 424 set_flags |= TSSF_TASK_USER_DIV; 425 get_flags |= TSSF_TASK_USER_DIV; 426 } else { 427 /* Otherwise we should use the random diversifier */ 428 set_flags |= TSSF_RANDOM_USER_DIV; 429 get_flags |= TSSF_RANDOM_USER_DIV; 430 } 431 } 432 433 c_thr_exc_raise_state_identity_token++; 434 kr = task_create_identity_token(task, &task_token); 435 436 if (!task->active && kr == KERN_INVALID_ARGUMENT) { 437 /* The task is terminating, don't need to send more exceptions */ 438 kr = KERN_SUCCESS; 439 goto out_release_right; 440 } 441 442 /* task_token now represents a task, or corpse */ 443 assert(kr == KERN_SUCCESS); 444 task_token_port = convert_task_id_token_to_port(task_token); 445 /* task token ref consumed */ 446 447 old_state_cnt = _MachineStateCount[flavor]; 448 kr = thread_getstatus_to_user(thread, flavor, 449 (thread_state_t)old_state, 450 &old_state_cnt, get_flags); 451 new_state_cnt = old_state_cnt; 452 453 if (kr == KERN_SUCCESS) { 454 new_state = (thread_state_t)kalloc_data(sizeof(thread_state_data_t), Z_WAITOK | Z_ZERO); 455 if (new_state == NULL) { 456 kr = KERN_RESOURCE_SHORTAGE; 457 goto out_release_right; 458 } 459 460 if (code64) { 461 kr = mach_exception_raise_state_identity_protected(exc_port, 462 thread->thread_id, 463 task_token_port, 464 exception, 465 code, 466 codeCnt, 467 &flavor, 468 old_state, old_state_cnt, 469 new_state, &new_state_cnt); 470 } else { 471 panic("mach_exception_raise_state_identity_protected() must be code64"); 472 } 473 474 if (kr == KERN_SUCCESS) { 475 if (exception != EXC_CORPSE_NOTIFY) { 476 kr = thread_setstatus_from_user(thread, flavor, 477 (thread_state_t)new_state, new_state_cnt, 478 (thread_state_t)old_state, old_state_cnt, set_flags); 479 } 480 goto out_release_right; 481 } 482 } 483 484 goto out_release_right; 485 } 486 487 case EXCEPTION_STATE_IDENTITY: { 488 mach_msg_type_number_t old_state_cnt, new_state_cnt; 489 thread_state_data_t old_state; 490 bool task_allow_user_state = task_needs_user_signed_thread_state(task); 491 492 if (pac_replace_ptrs_user || task_allow_user_state) { 493 get_flags |= TSSF_RANDOM_USER_DIV; 494 set_flags |= (TSSF_ALLOW_ONLY_USER_PTRS | TSSF_RANDOM_USER_DIV); 495 } 496 497 c_thr_exc_raise_state_id++; 498 499 task_reference(task); 500 thread_reference(thread); 501 /* 502 * Only deliver control port if Developer Mode enabled, 503 * or task is a corpse. Otherwise we only deliver the 504 * (immovable) read port in exception handler (both in 505 * or out of process). (94669540) 506 */ 507 if (developer_mode_state() || task_is_a_corpse(task)) { 508 task_port = convert_task_to_port(task); 509 thread_port = convert_thread_to_port(thread); 510 } else { 511 task_port = convert_task_read_to_port(task); 512 thread_port = convert_thread_read_to_port(thread); 513 } 514 /* task and thread ref consumed */ 515 516 assert(flavor < THREAD_STATE_FLAVORS); 517 old_state_cnt = (flavor < THREAD_STATE_FLAVORS) ? _MachineStateCount[flavor] : 0; 518 kr = thread_getstatus_to_user(thread, flavor, 519 (thread_state_t)old_state, 520 &old_state_cnt, get_flags); 521 new_state_cnt = old_state_cnt; 522 if (kr == KERN_SUCCESS) { 523 new_state = (thread_state_t)kalloc_data(sizeof(thread_state_data_t), Z_WAITOK | Z_ZERO); 524 if (new_state == NULL) { 525 kr = KERN_RESOURCE_SHORTAGE; 526 goto out_release_right; 527 } 528 if (code64) { 529 kr = mach_exception_raise_state_identity( 530 exc_port, 531 thread_port, 532 task_port, 533 exception, 534 code, 535 codeCnt, 536 &flavor, 537 old_state, old_state_cnt, 538 new_state, &new_state_cnt); 539 } else { 540 kr = exception_raise_state_identity(exc_port, 541 thread_port, 542 task_port, 543 exception, 544 small_code, 545 codeCnt, 546 &flavor, 547 old_state, old_state_cnt, 548 new_state, &new_state_cnt); 549 } 550 551 if (kr == KERN_SUCCESS) { 552 if (exception != EXC_CORPSE_NOTIFY && 553 ip_type(thread_port) == IKOT_THREAD_CONTROL) { 554 kr = thread_setstatus_from_user(thread, flavor, 555 (thread_state_t)new_state, new_state_cnt, 556 (thread_state_t)old_state, old_state_cnt, set_flags); 557 } 558 goto out_release_right; 559 } 560 } 561 562 goto out_release_right; 563 } 564 565 default: 566 panic("bad exception behavior!"); 567 return KERN_FAILURE; 568 }/* switch */ 569 570out_release_right: 571 572 thread->options &= ~TH_IN_MACH_EXCEPTION; 573 574 if (task_port) { 575 ipc_port_release_send(task_port); 576 } 577 578 if (thread_port) { 579 ipc_port_release_send(thread_port); 580 } 581 582 if (exc_port) { 583 ipc_port_release_send(exc_port); 584 } 585 586 if (task_token_port) { 587 ipc_port_release_send(task_token_port); 588 } 589 590 if (new_state) { 591 kfree_data(new_state, sizeof(thread_state_data_t)); 592 } 593 594 return kr; 595} 596 597/* 598 * Attempt exception delivery with backtrace info to exception ports 599 * in exc_ports in order. 600 */ 601/* 602 * Routine: exception_deliver_backtrace 603 * Purpose: 604 * Attempt exception delivery with backtrace info to exception ports 605 * in exc_ports in order. 606 * Conditions: 607 * Caller has a reference on bt_object, and send rights on exc_ports. 608 * Does not consume any passed references or rights 609 */ 610void 611exception_deliver_backtrace( 612 kcdata_object_t bt_object, 613 ipc_port_t exc_ports[static BT_EXC_PORTS_COUNT], 614 exception_type_t exception) 615{ 616 kern_return_t kr; 617 mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; 618 ipc_port_t target_port, bt_obj_port; 619 620 assert(exception == EXC_GUARD); 621 622 code[0] = exception; 623 code[1] = 0; 624 625 kcdata_object_reference(bt_object); 626 bt_obj_port = convert_kcdata_object_to_port(bt_object); 627 /* backtrace object ref consumed, no-senders is armed */ 628 629 if (!IP_VALID(bt_obj_port)) { 630 return; 631 } 632 633 /* 634 * We are guaranteed at task_enqueue_exception_with_corpse() time 635 * that the exception port prefers backtrace delivery. 636 */ 637 for (unsigned int i = 0; i < BT_EXC_PORTS_COUNT; i++) { 638 target_port = exc_ports[i]; 639 640 if (!IP_VALID(target_port)) { 641 continue; 642 } 643 644 ip_mq_lock(target_port); 645 if (!ip_active(target_port)) { 646 ip_mq_unlock(target_port); 647 continue; 648 } 649 ip_mq_unlock(target_port); 650 651 kr = mach_exception_raise_backtrace(target_port, 652 bt_obj_port, 653 EXC_CORPSE_NOTIFY, 654 code, 655 EXCEPTION_CODE_MAX); 656 657 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) { 658 /* Exception is handled at this level */ 659 break; 660 } 661 } 662 663 /* May trigger no-senders notification for backtrace object */ 664 ipc_port_release_send(bt_obj_port); 665 666 return; 667} 668 669/* 670 * Routine: check_exc_receiver_dependency 671 * Purpose: 672 * Verify that the port destined for receiving this exception is not 673 * on the current task. This would cause hang in kernel for 674 * EXC_CRASH primarily. Note: If port is transferred 675 * between check and delivery then deadlock may happen. 676 * 677 * Conditions: 678 * Nothing locked and no resources held. 679 * Called from an exception context. 680 * Returns: 681 * KERN_SUCCESS if its ok to send exception message. 682 */ 683static kern_return_t 684check_exc_receiver_dependency( 685 exception_type_t exception, 686 struct exception_action *excp, 687 lck_mtx_t *mutex) 688{ 689 kern_return_t retval = KERN_SUCCESS; 690 691 if (excp == NULL || exception != EXC_CRASH) { 692 return retval; 693 } 694 695 task_t task = current_task(); 696 lck_mtx_lock(mutex); 697 ipc_port_t xport = excp[exception].port; 698 if (IP_VALID(xport) && ip_in_space_noauth(xport, task->itk_space)) { 699 retval = KERN_FAILURE; 700 } 701 lck_mtx_unlock(mutex); 702 return retval; 703} 704 705 706/* 707 * Routine: exception_triage_thread 708 * Purpose: 709 * The thread caught an exception. 710 * We make an up-call to the thread's exception server. 711 * Conditions: 712 * Nothing locked and no resources held. 713 * Called from an exception context, so 714 * thread_exception_return and thread_kdb_return 715 * are possible. 716 * Returns: 717 * KERN_SUCCESS if exception is handled by any of the handlers. 718 */ 719kern_return_t 720exception_triage_thread( 721 exception_type_t exception, 722 mach_exception_data_t code, 723 mach_msg_type_number_t codeCnt, 724 thread_t thread) 725{ 726 task_t task; 727 thread_ro_t tro; 728 host_priv_t host_priv; 729 lck_mtx_t *mutex; 730 struct exception_action *actions; 731 kern_return_t kr = KERN_FAILURE; 732 733 assert(exception != EXC_RPC_ALERT); 734 735 /* 736 * If this behavior has been requested by the the kernel 737 * (due to the boot environment), we should panic if we 738 * enter this function. This is intended as a debugging 739 * aid; it should allow us to debug why we caught an 740 * exception in environments where debugging is especially 741 * difficult. 742 */ 743 if (panic_on_exception_triage) { 744 panic("called exception_triage when it was forbidden by the boot environment"); 745 } 746 747 /* 748 * Try to raise the exception at the activation level. 749 */ 750 mutex = &thread->mutex; 751 tro = get_thread_ro(thread); 752 actions = tro->tro_exc_actions; 753 if (KERN_SUCCESS == check_exc_receiver_dependency(exception, actions, mutex)) { 754 kr = exception_deliver(thread, exception, code, codeCnt, actions, mutex); 755 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) { 756 goto out; 757 } 758 } 759 760 /* 761 * Maybe the task level will handle it. 762 */ 763 task = tro->tro_task; 764 mutex = &task->itk_lock_data; 765 actions = task->exc_actions; 766 if (KERN_SUCCESS == check_exc_receiver_dependency(exception, actions, mutex)) { 767 kr = exception_deliver(thread, exception, code, codeCnt, actions, mutex); 768 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) { 769 goto out; 770 } 771 } 772 773 /* 774 * How about at the host level? 775 */ 776 host_priv = host_priv_self(); 777 mutex = &host_priv->lock; 778 actions = host_priv->exc_actions; 779 if (KERN_SUCCESS == check_exc_receiver_dependency(exception, actions, mutex)) { 780 kr = exception_deliver(thread, exception, code, codeCnt, actions, mutex); 781 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) { 782 goto out; 783 } 784 } 785 786out: 787 if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) && 788 (exception != EXC_GUARD) && (exception != EXC_CORPSE_NOTIFY)) { 789 thread_exception_return(); 790 } 791 return kr; 792} 793 794#if __has_feature(ptrauth_calls) 795static TUNABLE(bool, pac_exception_telemetry, "-pac_exception_telemetry", false); 796 797CA_EVENT(pac_exception_event, 798 CA_INT, exception, 799 CA_INT, exception_code_0, 800 CA_INT, exception_code_1, 801 CA_STATIC_STRING(CA_PROCNAME_LEN), proc_name); 802 803static void 804pac_exception_triage( 805 exception_type_t exception, 806 mach_exception_data_t code) 807{ 808 boolean_t traced_flag = FALSE; 809 task_t task = current_task(); 810 void *proc = get_bsdtask_info(task); 811 char *proc_name = (char *) "unknown"; 812 int pid = 0; 813 814#ifdef MACH_BSD 815 pid = proc_selfpid(); 816 if (proc) { 817 traced_flag = proc_is_traced(proc); 818 /* Should only be called on current proc */ 819 proc_name = proc_name_address(proc); 820 821 /* 822 * For a ptrauth violation, check if process isn't being ptraced and 823 * the task has the TFRO_PAC_EXC_FATAL flag set. If both conditions are true, 824 * terminate the task via exit_with_reason 825 */ 826 if (!traced_flag) { 827 if (pac_exception_telemetry) { 828 ca_event_t ca_event = CA_EVENT_ALLOCATE(pac_exception_event); 829 CA_EVENT_TYPE(pac_exception_event) * pexc_event = ca_event->data; 830 pexc_event->exception = exception; 831 pexc_event->exception_code_0 = code[0]; 832 pexc_event->exception_code_1 = code[1]; 833 strlcpy(pexc_event->proc_name, proc_name, CA_PROCNAME_LEN); 834 CA_EVENT_SEND(ca_event); 835 } 836 if (task_is_pac_exception_fatal(task)) { 837 os_log_error(OS_LOG_DEFAULT, "%s: process %s[%d] hit a pac violation\n", __func__, proc_name, pid); 838 839 exception_info_t info = { 840 .os_reason = OS_REASON_PAC_EXCEPTION, 841 .exception_type = exception, 842 .mx_code = code[0], 843 .mx_subcode = code[1] 844 }; 845 exit_with_mach_exception(proc, info, PX_FLAGS_NONE); 846 thread_exception_return(); 847 /* NOT_REACHABLE */ 848 } 849 } 850 } 851#endif /* MACH_BSD */ 852} 853#endif /* __has_feature(ptrauth_calls) */ 854 855static void 856maybe_unrecoverable_exception_triage( 857 exception_type_t exception, 858 mach_exception_data_t code) 859{ 860 task_t task = current_task(); 861 void *proc = get_bsdtask_info(task); 862 863#ifdef MACH_BSD 864 if (!proc) { 865 return; 866 } 867 868 /* 869 * Note that the below policy to decide whether this should be unrecoverable is 870 * likely conceptually specific to the particular exception. 871 * If you find yourself adding another user_brk_..._descriptor and want to customize the 872 * policy for whether it should be unrecoverable, consider attaching each policy to 873 * the corresponding descriptor and somehow carrying it through to here. 874 */ 875 /* These exceptions are deliverable (and potentially recoverable) if the process is being debugged. */ 876 if (is_address_space_debugged(proc)) { 877 return; 878 } 879 880 /* 881 * By policy, this exception is uncatchable by exception/signal handlers. 882 * Therefore exit immediately. 883 */ 884 /* Should only be called on current proc */ 885 int pid = proc_selfpid(); 886 char *proc_name = proc_name_address(proc); 887 os_log_error(OS_LOG_DEFAULT, "%s: process %s[%d] hit an unrecoverable exception\n", __func__, proc_name, pid); 888 889 exception_info_t info = { 890 /* 891 * For now, hard-code this to OS_REASON_FOUNDATION as that's the path we expect to be on today. 892 * In the future this should probably be carried by the user_brk_..._descriptor and piped through. 893 */ 894 .os_reason = OS_REASON_FOUNDATION, 895 .exception_type = exception, 896 .mx_code = code[0], 897 .mx_subcode = code[1] 898 }; 899 exit_with_mach_exception(proc, info, PX_FLAGS_NONE); 900 thread_exception_return(); 901 /* NOT_REACHABLE */ 902#endif /* MACH_BSD */ 903} 904 905/* 906 * Routine: exception_triage 907 * Purpose: 908 * The current thread caught an exception. 909 * We make an up-call to the thread's exception server. 910 * Conditions: 911 * Nothing locked and no resources held. 912 * Called from an exception context, so 913 * thread_exception_return and thread_kdb_return 914 * are possible. 915 * Returns: 916 * KERN_SUCCESS if exception is handled by any of the handlers. 917 */ 918int debug4k_panic_on_exception = 0; 919kern_return_t 920exception_triage( 921 exception_type_t exception, 922 mach_exception_data_t code, 923 mach_msg_type_number_t codeCnt) 924{ 925 thread_t thread = current_thread(); 926 task_t task = current_task(); 927 928 assert(codeCnt > 0); 929 930 if (VM_MAP_PAGE_SIZE(task->map) < PAGE_SIZE) { 931 DEBUG4K_EXC("thread %p task %p map %p exception %d codes 0x%llx 0x%llx\n", 932 thread, task, task->map, exception, code[0], codeCnt > 1 ? code[1] : 0); 933 if (debug4k_panic_on_exception) { 934 panic("DEBUG4K thread %p task %p map %p exception %d codes 0x%llx 0x%llx", 935 thread, task, task->map, exception, code[0], codeCnt > 1 ? code[1] : 0); 936 } 937 } 938 939#if DEVELOPMENT || DEBUG 940#ifdef MACH_BSD 941 if (proc_pid(get_bsdtask_info(task)) <= exception_log_max_pid) { 942 record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_PROCESS, "process exit", 943 "exception_log_max_pid: pid %d (%s): sending exception %d (0x%llx 0x%llx)", 944 proc_pid(get_bsdtask_info(task)), proc_name_address(get_bsdtask_info(task)), 945 exception, code[0], codeCnt > 1 ? code[1] : 0); 946 } 947#endif /* MACH_BSD */ 948#endif /* DEVELOPMENT || DEBUG */ 949 950#if __has_feature(ptrauth_calls) 951 if (exception & EXC_PTRAUTH_BIT) { 952 exception &= ~EXC_PTRAUTH_BIT; 953 assert(codeCnt == 2); 954 /* Note this may consume control flow if it decides the exception is unrecoverable. */ 955 pac_exception_triage(exception, code); 956 } 957#endif /* __has_feature(ptrauth_calls) */ 958 if (exception & EXC_MAY_BE_UNRECOVERABLE_BIT) { 959 exception &= ~EXC_MAY_BE_UNRECOVERABLE_BIT; 960 assert(codeCnt == 2); 961 /* Note this may consume control flow if it decides the exception is unrecoverable. */ 962 maybe_unrecoverable_exception_triage(exception, code); 963 } 964 return exception_triage_thread(exception, code, codeCnt, thread); 965} 966 967kern_return_t 968bsd_exception( 969 exception_type_t exception, 970 mach_exception_data_t code, 971 mach_msg_type_number_t codeCnt) 972{ 973 task_t task; 974 lck_mtx_t *mutex; 975 thread_t self = current_thread(); 976 kern_return_t kr; 977 978 /* 979 * Maybe the task level will handle it. 980 */ 981 task = current_task(); 982 mutex = &task->itk_lock_data; 983 984 kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex); 985 986 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) { 987 return KERN_SUCCESS; 988 } 989 return KERN_FAILURE; 990} 991 992 993/* 994 * Raise an exception on a task. 995 * This should tell launchd to launch Crash Reporter for this task. 996 * If the exception is fatal, we should be careful about sending a synchronous exception 997 */ 998kern_return_t 999task_exception_notify(exception_type_t exception, 1000 mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode, const bool fatal) 1001{ 1002 mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; 1003 wait_interrupt_t wsave; 1004 kern_return_t kr = KERN_SUCCESS; 1005 1006 /* 1007 * If we are not in dev mode, nobody should be allowed to synchronously handle 1008 * a fatal EXC_GUARD - they might stall on it indefinitely 1009 */ 1010 if (fatal && !developer_mode_state() && exception == EXC_GUARD) { 1011 return KERN_DENIED; 1012 } 1013 1014 code[0] = exccode; 1015 code[1] = excsubcode; 1016 1017 wsave = thread_interrupt_level(THREAD_UNINT); 1018 kr = exception_triage(exception, code, EXCEPTION_CODE_MAX); 1019 (void) thread_interrupt_level(wsave); 1020 return kr; 1021} 1022 1023 1024/* 1025 * Handle interface for special performance monitoring 1026 * This is a special case of the host exception handler 1027 */ 1028kern_return_t 1029sys_perf_notify(thread_t thread, int pid) 1030{ 1031 host_priv_t hostp; 1032 ipc_port_t xport; 1033 struct exception_action saved_exc_actions[EXC_TYPES_COUNT] = {}; 1034 wait_interrupt_t wsave; 1035 kern_return_t ret; 1036 struct label *temp_label; 1037 1038 hostp = host_priv_self(); /* Get the host privileged ports */ 1039 mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; 1040 code[0] = 0xFF000001; /* Set terminate code */ 1041 code[1] = pid; /* Pass out the pid */ 1042 1043#if CONFIG_MACF 1044 /* Create new label for saved_exc_actions[EXC_RPC_ALERT] */ 1045 mac_exc_associate_action_label(&saved_exc_actions[EXC_RPC_ALERT], 1046 mac_exc_create_label(&saved_exc_actions[EXC_RPC_ALERT])); 1047#endif /* CONFIG_MACF */ 1048 1049 lck_mtx_lock(&hostp->lock); 1050 xport = hostp->exc_actions[EXC_RPC_ALERT].port; 1051 1052 /* Make sure we're not catching our own exception */ 1053 if (!IP_VALID(xport) || 1054 !ip_active(xport) || 1055 ip_in_space_noauth(xport, get_threadtask(thread)->itk_space)) { 1056 lck_mtx_unlock(&hostp->lock); 1057#if CONFIG_MACF 1058 mac_exc_free_action_label(&saved_exc_actions[EXC_RPC_ALERT]); 1059#endif /* CONFIG_MACF */ 1060 return KERN_FAILURE; 1061 } 1062 1063 /* Save hostp->exc_actions and hold a sright to xport so it can't be dropped after unlock */ 1064 temp_label = saved_exc_actions[EXC_RPC_ALERT].label; 1065 saved_exc_actions[EXC_RPC_ALERT] = hostp->exc_actions[EXC_RPC_ALERT]; 1066 saved_exc_actions[EXC_RPC_ALERT].port = exception_port_copy_send(xport); 1067 saved_exc_actions[EXC_RPC_ALERT].label = temp_label; 1068 1069#if CONFIG_MACF 1070 mac_exc_inherit_action_label(&hostp->exc_actions[EXC_RPC_ALERT], &saved_exc_actions[EXC_RPC_ALERT]); 1071#endif /* CONFIG_MACF */ 1072 1073 lck_mtx_unlock(&hostp->lock); 1074 1075 wsave = thread_interrupt_level(THREAD_UNINT); 1076 ret = exception_deliver( 1077 thread, 1078 EXC_RPC_ALERT, 1079 code, 1080 2, 1081 saved_exc_actions, 1082 &hostp->lock); 1083 (void)thread_interrupt_level(wsave); 1084 1085#if CONFIG_MACF 1086 mac_exc_free_action_label(&saved_exc_actions[EXC_RPC_ALERT]); 1087#endif /* CONFIG_MACF */ 1088 ipc_port_release_send(saved_exc_actions[EXC_RPC_ALERT].port); 1089 1090 return ret; 1091}