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