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