]> git.saurik.com Git - apple/xnu.git/blob - bsd/uxkern/ux_exception.c
xnu-1699.26.8.tar.gz
[apple/xnu.git] / bsd / uxkern / ux_exception.c
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 */
67 struct ipc_object;
68 extern 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);
70 extern 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);
75 extern 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);
78 extern thread_t convert_port_to_thread(ipc_port_t port);
79 extern void ipc_port_release(ipc_port_t);
80
81
82
83
84
85 /*
86 * Unix exception handler.
87 */
88
89 static 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__)
94 mach_port_t ux_exception_port;
95 #else
96 mach_port_name_t ux_exception_port;
97 #endif /* __x86_64__ */
98
99 static task_t ux_handler_self;
100
101 static
102 void
103 ux_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
191 void
192 ux_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
206 kern_return_t
207 catch_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
229 kern_return_t
230 catch_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 task *sig_task;
243 struct proc *p;
244 kern_return_t result = MACH_MSG_SUCCESS;
245 int ux_signal = 0;
246 mach_exception_code_t ucode = 0;
247 struct uthread *ut;
248 mach_port_name_t thread_name = CAST_MACH_PORT_TO_NAME(thread);
249 mach_port_name_t task_name = CAST_MACH_PORT_TO_NAME(task);
250
251 /*
252 * Convert local thread name to global port.
253 */
254 if (MACH_PORT_VALID(thread_name) &&
255 (ipc_object_copyin(get_task_ipcspace(self), thread_name,
256 MACH_MSG_TYPE_PORT_SEND,
257 (void *) &thread_port) == MACH_MSG_SUCCESS)) {
258 if (IPC_PORT_VALID(thread_port)) {
259 th_act = convert_port_to_thread(thread_port);
260 ipc_port_release(thread_port);
261 } else {
262 th_act = THREAD_NULL;
263 }
264
265 /*
266 * Catch bogus ports
267 */
268 if (th_act != THREAD_NULL) {
269
270 /*
271 * Convert exception to unix signal and code.
272 */
273 ux_exception(exception, code[0], code[1], &ux_signal, &ucode);
274
275 ut = get_bsdthread_info(th_act);
276 sig_task = get_threadtask(th_act);
277 p = (struct proc *) get_bsdtask_info(sig_task);
278
279 /* Can't deliver a signal without a bsd process */
280 if (p == NULL) {
281 ux_signal = 0;
282 result = KERN_FAILURE;
283 }
284
285 /*
286 * Stack overflow should result in a SIGSEGV signal
287 * on the alternate stack.
288 * but we have one or more guard pages after the
289 * stack top, so we would get a KERN_PROTECTION_FAILURE
290 * exception instead of KERN_INVALID_ADDRESS, resulting in
291 * a SIGBUS signal.
292 * Detect that situation and select the correct signal.
293 */
294 if (code[0] == KERN_PROTECTION_FAILURE &&
295 ux_signal == SIGBUS) {
296 user_addr_t sp, stack_min, stack_max;
297 int mask;
298 struct sigacts *ps;
299
300 sp = code[1];
301 if (ut && (ut->uu_flag & UT_VFORK))
302 p = ut->uu_proc;
303 #if STACK_GROWTH_UP
304 stack_min = p->user_stack;
305 stack_max = p->user_stack + MAXSSIZ;
306 #else /* STACK_GROWTH_UP */
307 stack_max = p->user_stack;
308 stack_min = p->user_stack - MAXSSIZ;
309 #endif /* STACK_GROWTH_UP */
310 if (sp >= stack_min &&
311 sp < stack_max) {
312 /*
313 * This is indeed a stack overflow. Deliver a
314 * SIGSEGV signal.
315 */
316 ux_signal = SIGSEGV;
317
318 /*
319 * If the thread/process is not ready to handle
320 * SIGSEGV on an alternate stack, force-deliver
321 * SIGSEGV with a SIG_DFL handler.
322 */
323 mask = sigmask(ux_signal);
324 ps = p->p_sigacts;
325 if ((p->p_sigignore & mask) ||
326 (ut->uu_sigwait & mask) ||
327 (ut->uu_sigmask & mask) ||
328 (ps->ps_sigact[SIGSEGV] == SIG_IGN) ||
329 (! (ps->ps_sigonstack & mask))) {
330 p->p_sigignore &= ~mask;
331 p->p_sigcatch &= ~mask;
332 ps->ps_sigact[SIGSEGV] = SIG_DFL;
333 ut->uu_sigwait &= ~mask;
334 ut->uu_sigmask &= ~mask;
335 }
336 }
337 }
338 /*
339 * Send signal.
340 */
341 if (ux_signal != 0) {
342 ut->uu_exception = exception;
343 //ut->uu_code = code[0]; // filled in by threadsignal
344 ut->uu_subcode = code[1];
345 threadsignal(th_act, ux_signal, code[0]);
346 }
347
348 thread_deallocate(th_act);
349 }
350 else
351 result = KERN_INVALID_ARGUMENT;
352 }
353 else
354 result = KERN_INVALID_ARGUMENT;
355
356 /*
357 * Delete our send rights to the task port.
358 */
359 (void)mach_port_deallocate(get_task_ipcspace(ux_handler_self), task_name);
360
361 return (result);
362 }
363
364 kern_return_t
365 catch_exception_raise_state(
366 __unused mach_port_t exception_port,
367 __unused exception_type_t exception,
368 __unused const exception_data_t code,
369 __unused mach_msg_type_number_t codeCnt,
370 __unused int *flavor,
371 __unused const thread_state_t old_state,
372 __unused mach_msg_type_number_t old_stateCnt,
373 __unused thread_state_t new_state,
374 __unused mach_msg_type_number_t *new_stateCnt)
375 {
376 return(KERN_INVALID_ARGUMENT);
377 }
378
379 kern_return_t
380 catch_mach_exception_raise_state(
381 __unused mach_port_t exception_port,
382 __unused exception_type_t exception,
383 __unused const mach_exception_data_t code,
384 __unused mach_msg_type_number_t codeCnt,
385 __unused int *flavor,
386 __unused const thread_state_t old_state,
387 __unused mach_msg_type_number_t old_stateCnt,
388 __unused thread_state_t new_state,
389 __unused mach_msg_type_number_t *new_stateCnt)
390 {
391 return(KERN_INVALID_ARGUMENT);
392 }
393
394 kern_return_t
395 catch_exception_raise_state_identity(
396 __unused mach_port_t exception_port,
397 __unused mach_port_t thread,
398 __unused mach_port_t task,
399 __unused exception_type_t exception,
400 __unused exception_data_t code,
401 __unused mach_msg_type_number_t codeCnt,
402 __unused int *flavor,
403 __unused thread_state_t old_state,
404 __unused mach_msg_type_number_t old_stateCnt,
405 __unused thread_state_t new_state,
406 __unused mach_msg_type_number_t *new_stateCnt)
407 {
408 return(KERN_INVALID_ARGUMENT);
409 }
410
411 kern_return_t
412 catch_mach_exception_raise_state_identity(
413 __unused mach_port_t exception_port,
414 __unused mach_port_t thread,
415 __unused mach_port_t task,
416 __unused exception_type_t exception,
417 __unused mach_exception_data_t code,
418 __unused mach_msg_type_number_t codeCnt,
419 __unused int *flavor,
420 __unused thread_state_t old_state,
421 __unused mach_msg_type_number_t old_stateCnt,
422 __unused thread_state_t new_state,
423 __unused mach_msg_type_number_t *new_stateCnt)
424 {
425 return(KERN_INVALID_ARGUMENT);
426 }
427
428
429 /*
430 * ux_exception translates a mach exception, code and subcode to
431 * a signal and u.u_code. Calls machine_exception (machine dependent)
432 * to attempt translation first.
433 */
434
435 static
436 void ux_exception(
437 int exception,
438 mach_exception_code_t code,
439 mach_exception_subcode_t subcode,
440 int *ux_signal,
441 mach_exception_code_t *ux_code)
442 {
443 /*
444 * Try machine-dependent translation first.
445 */
446 if (machine_exception(exception, code, subcode, ux_signal, ux_code))
447 return;
448
449 switch(exception) {
450
451 case EXC_BAD_ACCESS:
452 if (code == KERN_INVALID_ADDRESS)
453 *ux_signal = SIGSEGV;
454 else
455 *ux_signal = SIGBUS;
456 break;
457
458 case EXC_BAD_INSTRUCTION:
459 *ux_signal = SIGILL;
460 break;
461
462 case EXC_ARITHMETIC:
463 *ux_signal = SIGFPE;
464 break;
465
466 case EXC_EMULATION:
467 *ux_signal = SIGEMT;
468 break;
469
470 case EXC_SOFTWARE:
471 switch (code) {
472
473 case EXC_UNIX_BAD_SYSCALL:
474 *ux_signal = SIGSYS;
475 break;
476 case EXC_UNIX_BAD_PIPE:
477 *ux_signal = SIGPIPE;
478 break;
479 case EXC_UNIX_ABORT:
480 *ux_signal = SIGABRT;
481 break;
482 case EXC_SOFT_SIGNAL:
483 *ux_signal = SIGKILL;
484 break;
485 }
486 break;
487
488 case EXC_BREAKPOINT:
489 *ux_signal = SIGTRAP;
490 break;
491 }
492 }