1/* 2 * Copyright (c) 2000-2008 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 * Mach Operating System 30 * Copyright (c) 1987 Carnegie-Mellon University 31 * All rights reserved. The CMU software License Agreement specifies 32 * the terms and conditions for use and redistribution. 33 */ 34 35/* 36 ********************************************************************* 37 * HISTORY 38 ********************************************************************** 39 */ 40 41#include <sys/param.h> 42 43#include <mach/boolean.h> 44#include <mach/exception.h> 45#include <mach/kern_return.h> 46#include <mach/message.h> 47#include <mach/port.h> 48#include <mach/mach_port.h> 49#include <mach/mig_errors.h> 50#include <mach/exc_server.h> 51#include <mach/mach_exc_server.h> 52#include <kern/task.h> 53#include <kern/thread.h> 54#include <kern/sched_prim.h> 55#include <kern/kalloc.h> 56 57#include <sys/proc.h> 58#include <sys/user.h> 59#include <sys/systm.h> 60#include <sys/ux_exception.h> 61#include <sys/vmparam.h> /* MAXSSIZ */ 62 63#include <vm/vm_protos.h> /* get_task_ipcspace() */ 64/* 65 * XXX Things that should be retrieved from Mach headers, but aren't 66 */ 67struct ipc_object; 68extern kern_return_t ipc_object_copyin(ipc_space_t space, mach_port_name_t name, 69 mach_msg_type_name_t msgt_name, struct ipc_object **objectp); 70extern mach_msg_return_t mach_msg_receive(mach_msg_header_t *msg, 71 mach_msg_option_t option, mach_msg_size_t rcv_size, 72 mach_port_name_t rcv_name, mach_msg_timeout_t rcv_timeout, 73 void (*continuation)(mach_msg_return_t), 74 mach_msg_size_t slist_size); 75extern mach_msg_return_t mach_msg_send(mach_msg_header_t *msg, 76 mach_msg_option_t option, mach_msg_size_t send_size, 77 mach_msg_timeout_t send_timeout, mach_port_name_t notify); 78extern thread_t convert_port_to_thread(ipc_port_t port); 79extern void ipc_port_release(ipc_port_t); 80 81 82 83 84 85/* 86 * Unix exception handler. 87 */ 88 89static void ux_exception(int exception, mach_exception_code_t code, 90 mach_exception_subcode_t subcode, 91 int *ux_signal, mach_exception_code_t *ux_code); 92 93#if defined(__x86_64__) || defined(__arm64__) 94mach_port_t ux_exception_port; 95#else 96mach_port_name_t ux_exception_port; 97#endif /* __x86_64__ */ 98 99static task_t ux_handler_self; 100 101static 102void 103ux_handler(void) 104{ 105 task_t self = current_task(); 106 mach_port_name_t exc_port_name; 107 mach_port_name_t exc_set_name; 108 109 /* self->kernel_vm_space = TRUE; */ 110 ux_handler_self = self; 111 112 113 /* 114 * Allocate a port set that we will receive on. 115 */ 116 if (mach_port_allocate(get_task_ipcspace(ux_handler_self), MACH_PORT_RIGHT_PORT_SET, &exc_set_name) != MACH_MSG_SUCCESS) 117 panic("ux_handler: port_set_allocate failed"); 118 119 /* 120 * Allocate an exception port and use object_copyin to 121 * translate it to the global name. Put it into the set. 122 */ 123 if (mach_port_allocate(get_task_ipcspace(ux_handler_self), MACH_PORT_RIGHT_RECEIVE, &exc_port_name) != MACH_MSG_SUCCESS) 124 panic("ux_handler: port_allocate failed"); 125 if (mach_port_move_member(get_task_ipcspace(ux_handler_self), 126 exc_port_name, exc_set_name) != MACH_MSG_SUCCESS) 127 panic("ux_handler: port_set_add failed"); 128 129 if (ipc_object_copyin(get_task_ipcspace(self), exc_port_name, 130 MACH_MSG_TYPE_MAKE_SEND, 131 (void *) &ux_exception_port) != MACH_MSG_SUCCESS) 132 panic("ux_handler: object_copyin(ux_exception_port) failed"); 133 134 proc_list_lock(); 135 thread_wakeup(&ux_exception_port); 136 proc_list_unlock(); 137 138 /* Message handling loop. */ 139 140 for (;;) { 141 struct rep_msg { 142 mach_msg_header_t Head; 143 NDR_record_t NDR; 144 kern_return_t RetCode; 145 } rep_msg; 146 struct exc_msg { 147 mach_msg_header_t Head; 148 /* start of the kernel processed data */ 149 mach_msg_body_t msgh_body; 150 mach_msg_port_descriptor_t thread; 151 mach_msg_port_descriptor_t task; 152 /* end of the kernel processed data */ 153 NDR_record_t NDR; 154 exception_type_t exception; 155 mach_msg_type_number_t codeCnt; 156 mach_exception_data_t code; 157 /* some times RCV_TO_LARGE probs */ 158 char pad[512]; 159 } exc_msg; 160 mach_port_name_t reply_port; 161 kern_return_t result; 162 163 exc_msg.Head.msgh_local_port = CAST_MACH_NAME_TO_PORT(exc_set_name); 164 exc_msg.Head.msgh_size = sizeof (exc_msg); 165#if 0 166 result = mach_msg_receive(&exc_msg.Head); 167#else 168 result = mach_msg_receive(&exc_msg.Head, MACH_RCV_MSG, 169 sizeof (exc_msg), exc_set_name, 170 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, 171 0); 172#endif 173 if (result == MACH_MSG_SUCCESS) { 174 reply_port = CAST_MACH_PORT_TO_NAME(exc_msg.Head.msgh_remote_port); 175 176 if (mach_exc_server(&exc_msg.Head, &rep_msg.Head)) { 177 result = mach_msg_send(&rep_msg.Head, MACH_SEND_MSG, 178 sizeof (rep_msg),MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL); 179 if (reply_port != 0 && result != MACH_MSG_SUCCESS) 180 mach_port_deallocate(get_task_ipcspace(ux_handler_self), reply_port); 181 } 182 183 } 184 else if (result == MACH_RCV_TOO_LARGE) 185 /* ignore oversized messages */; 186 else 187 panic("exception_handler"); 188 } 189} 190 191void 192ux_handler_init(void) 193{ 194 thread_t thread = THREAD_NULL; 195 196 ux_exception_port = MACH_PORT_NULL; 197 (void) kernel_thread_start((thread_continue_t)ux_handler, NULL, &thread); 198 thread_deallocate(thread); 199 proc_list_lock(); 200 if (ux_exception_port == MACH_PORT_NULL) { 201 (void)msleep(&ux_exception_port, proc_list_mlock, 0, "ux_handler_wait", 0); 202 } 203 proc_list_unlock(); 204} 205 206kern_return_t 207catch_exception_raise( 208 __unused mach_port_t exception_port, 209 mach_port_t thread, 210 mach_port_t task, 211 exception_type_t exception, 212 exception_data_t code, 213 __unused mach_msg_type_number_t codeCnt 214) 215{ 216 mach_exception_data_type_t big_code[EXCEPTION_CODE_MAX]; 217 big_code[0] = code[0]; 218 big_code[1] = code[1]; 219 220 return catch_mach_exception_raise(exception_port, 221 thread, 222 task, 223 exception, 224 big_code, 225 codeCnt); 226 227} 228 229kern_return_t 230catch_mach_exception_raise( 231 __unused mach_port_t exception_port, 232 mach_port_t thread, 233 mach_port_t task, 234 exception_type_t exception, 235 mach_exception_data_t code, 236 __unused mach_msg_type_number_t codeCnt 237) 238{ 239 task_t self = current_task(); 240 thread_t th_act; 241 ipc_port_t thread_port; 242 struct proc *p; 243 kern_return_t result = MACH_MSG_SUCCESS; 244 int ux_signal = 0; 245 mach_exception_code_t ucode = 0; 246 struct uthread *ut; 247 mach_port_name_t thread_name = CAST_MACH_PORT_TO_NAME(thread); 248 mach_port_name_t task_name = CAST_MACH_PORT_TO_NAME(task); 249 250 /* 251 * Convert local thread name to global port. 252 */ 253 if (MACH_PORT_VALID(thread_name) && 254 (ipc_object_copyin(get_task_ipcspace(self), thread_name, 255 MACH_MSG_TYPE_PORT_SEND, 256 (void *) &thread_port) == MACH_MSG_SUCCESS)) { 257 if (IPC_PORT_VALID(thread_port)) { 258 th_act = convert_port_to_thread(thread_port); 259 ipc_port_release(thread_port); 260 } else { 261 th_act = THREAD_NULL; 262 } 263 264 /* 265 * Catch bogus ports 266 */ 267 if (th_act != THREAD_NULL) { 268 269 /* 270 * Convert exception to unix signal and code. 271 */ 272 ux_exception(exception, code[0], code[1], &ux_signal, &ucode); 273 274 ut = get_bsdthread_info(th_act); 275 p = proc_findthread(th_act); 276 277 /* Can't deliver a signal without a bsd process reference */ 278 if (p == NULL) { 279 ux_signal = 0; 280 result = KERN_FAILURE; 281 } 282 283 /* 284 * Stack overflow should result in a SIGSEGV signal 285 * on the alternate stack. 286 * but we have one or more guard pages after the 287 * stack top, so we would get a KERN_PROTECTION_FAILURE 288 * exception instead of KERN_INVALID_ADDRESS, resulting in 289 * a SIGBUS signal. 290 * Detect that situation and select the correct signal. 291 */ 292 if (code[0] == KERN_PROTECTION_FAILURE && 293 ux_signal == SIGBUS) { 294 user_addr_t sp, stack_min, stack_max; 295 int mask; 296 struct sigacts *ps; 297 298 sp = code[1]; 299 300 stack_max = p->user_stack; 301 stack_min = p->user_stack - MAXSSIZ; 302 if (sp >= stack_min && 303 sp < stack_max) { 304 /* 305 * This is indeed a stack overflow. Deliver a 306 * SIGSEGV signal. 307 */ 308 ux_signal = SIGSEGV; 309 310 /* 311 * If the thread/process is not ready to handle 312 * SIGSEGV on an alternate stack, force-deliver 313 * SIGSEGV with a SIG_DFL handler. 314 */ 315 mask = sigmask(ux_signal); 316 ps = p->p_sigacts; 317 if ((p->p_sigignore & mask) || 318 (ut->uu_sigwait & mask) || 319 (ut->uu_sigmask & mask) || 320 (ps->ps_sigact[SIGSEGV] == SIG_IGN) || 321 (! (ps->ps_sigonstack & mask))) { 322 p->p_sigignore &= ~mask; 323 p->p_sigcatch &= ~mask; 324 ps->ps_sigact[SIGSEGV] = SIG_DFL; 325 ut->uu_sigwait &= ~mask; 326 ut->uu_sigmask &= ~mask; 327 } 328 } 329 } 330 /* 331 * Send signal. 332 */ 333 if (ux_signal != 0) { 334 ut->uu_exception = exception; 335 //ut->uu_code = code[0]; // filled in by threadsignal 336 ut->uu_subcode = code[1]; 337 threadsignal(th_act, ux_signal, code[0]); 338 } 339 if (p != NULL) 340 proc_rele(p); 341 thread_deallocate(th_act); 342 } 343 else 344 result = KERN_INVALID_ARGUMENT; 345 } 346 else 347 result = KERN_INVALID_ARGUMENT; 348 349 /* 350 * Delete our send rights to the task port. 351 */ 352 (void)mach_port_deallocate(get_task_ipcspace(ux_handler_self), task_name); 353 354 return (result); 355} 356 357kern_return_t 358catch_exception_raise_state( 359 __unused mach_port_t exception_port, 360 __unused exception_type_t exception, 361 __unused const exception_data_t code, 362 __unused mach_msg_type_number_t codeCnt, 363 __unused int *flavor, 364 __unused const thread_state_t old_state, 365 __unused mach_msg_type_number_t old_stateCnt, 366 __unused thread_state_t new_state, 367 __unused mach_msg_type_number_t *new_stateCnt) 368{ 369 return(KERN_INVALID_ARGUMENT); 370} 371 372kern_return_t 373catch_mach_exception_raise_state( 374 __unused mach_port_t exception_port, 375 __unused exception_type_t exception, 376 __unused const mach_exception_data_t code, 377 __unused mach_msg_type_number_t codeCnt, 378 __unused int *flavor, 379 __unused const thread_state_t old_state, 380 __unused mach_msg_type_number_t old_stateCnt, 381 __unused thread_state_t new_state, 382 __unused mach_msg_type_number_t *new_stateCnt) 383{ 384 return(KERN_INVALID_ARGUMENT); 385} 386 387kern_return_t 388catch_exception_raise_state_identity( 389 __unused mach_port_t exception_port, 390 __unused mach_port_t thread, 391 __unused mach_port_t task, 392 __unused exception_type_t exception, 393 __unused exception_data_t code, 394 __unused mach_msg_type_number_t codeCnt, 395 __unused int *flavor, 396 __unused thread_state_t old_state, 397 __unused mach_msg_type_number_t old_stateCnt, 398 __unused thread_state_t new_state, 399 __unused mach_msg_type_number_t *new_stateCnt) 400{ 401 return(KERN_INVALID_ARGUMENT); 402} 403 404kern_return_t 405catch_mach_exception_raise_state_identity( 406 __unused mach_port_t exception_port, 407 __unused mach_port_t thread, 408 __unused mach_port_t task, 409 __unused exception_type_t exception, 410 __unused mach_exception_data_t code, 411 __unused mach_msg_type_number_t codeCnt, 412 __unused int *flavor, 413 __unused thread_state_t old_state, 414 __unused mach_msg_type_number_t old_stateCnt, 415 __unused thread_state_t new_state, 416 __unused mach_msg_type_number_t *new_stateCnt) 417{ 418 return(KERN_INVALID_ARGUMENT); 419} 420 421 422/* 423 * ux_exception translates a mach exception, code and subcode to 424 * a signal and u.u_code. Calls machine_exception (machine dependent) 425 * to attempt translation first. 426 */ 427 428static 429void ux_exception( 430 int exception, 431 mach_exception_code_t code, 432 mach_exception_subcode_t subcode, 433 int *ux_signal, 434 mach_exception_code_t *ux_code) 435{ 436 /* 437 * Try machine-dependent translation first. 438 */ 439 if (machine_exception(exception, code, subcode, ux_signal, ux_code)) 440 return; 441 442 switch(exception) { 443 444 case EXC_BAD_ACCESS: 445 if (code == KERN_INVALID_ADDRESS) 446 *ux_signal = SIGSEGV; 447 else 448 *ux_signal = SIGBUS; 449 break; 450 451 case EXC_BAD_INSTRUCTION: 452 *ux_signal = SIGILL; 453 break; 454 455 case EXC_ARITHMETIC: 456 *ux_signal = SIGFPE; 457 break; 458 459 case EXC_EMULATION: 460 *ux_signal = SIGEMT; 461 break; 462 463 case EXC_SOFTWARE: 464 switch (code) { 465 466 case EXC_UNIX_BAD_SYSCALL: 467 *ux_signal = SIGSYS; 468 break; 469 case EXC_UNIX_BAD_PIPE: 470 *ux_signal = SIGPIPE; 471 break; 472 case EXC_UNIX_ABORT: 473 *ux_signal = SIGABRT; 474 break; 475 case EXC_SOFT_SIGNAL: 476 *ux_signal = SIGKILL; 477 break; 478 } 479 break; 480 481 case EXC_BREAKPOINT: 482 *ux_signal = SIGTRAP; 483 break; 484 } 485} 486