/ duct-tape / xnu / osfmk / kern / exception.c
exception.c
  1  /*
  2   * Copyright (c) 2000-2020 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_space.h>
 76  #include <ipc/ipc_pset.h>
 77  #include <ipc/ipc_machdep.h>
 78  
 79  #include <kern/ipc_tt.h>
 80  #include <kern/task.h>
 81  #include <kern/thread.h>
 82  #include <kern/processor.h>
 83  #include <kern/sched.h>
 84  #include <kern/sched_prim.h>
 85  #include <kern/host.h>
 86  #include <kern/misc_protos.h>
 87  #include <kern/ux_handler.h>
 88  
 89  #include <vm/vm_map.h>
 90  
 91  #include <security/mac_mach_internal.h>
 92  #include <string.h>
 93  
 94  #include <pexpert/pexpert.h>
 95  
 96  bool panic_on_exception_triage = false;
 97  
 98  unsigned long c_thr_exc_raise = 0;
 99  unsigned long c_thr_exc_raise_state = 0;
100  unsigned long c_thr_exc_raise_state_id = 0;
101  unsigned long c_tsk_exc_raise = 0;
102  unsigned long c_tsk_exc_raise_state = 0;
103  unsigned long c_tsk_exc_raise_state_id = 0;
104  
105  /* forward declarations */
106  kern_return_t exception_deliver(
107  	thread_t                thread,
108  	exception_type_t        exception,
109  	mach_exception_data_t   code,
110  	mach_msg_type_number_t  codeCnt,
111  	struct exception_action *excp,
112  	lck_mtx_t                       *mutex);
113  
114  static kern_return_t
115  check_exc_receiver_dependency(
116  	exception_type_t exception,
117  	struct exception_action *excp,
118  	lck_mtx_t *mutex);
119  
120  #ifdef MACH_BSD
121  kern_return_t bsd_exception(
122  	exception_type_t        exception,
123  	mach_exception_data_t   code,
124  	mach_msg_type_number_t  codeCnt);
125  #endif /* MACH_BSD */
126  
127  #if __has_feature(ptrauth_calls)
128  extern int exit_with_pac_exception(
129  	void *proc,
130  	exception_type_t         exception,
131  	mach_exception_code_t    code,
132  	mach_exception_subcode_t subcode);
133  
134  extern bool proc_is_traced(void *p);
135  #endif /* __has_feature(ptrauth_calls) */
136  
137  /*
138   * Routine: exception_init
139   * Purpose:
140   *   Global initialization of state for exceptions.
141   * Conditions:
142   *   None.
143   */
144  void
145  exception_init(void)
146  {
147  	int tmp = 0;
148  
149  	if (PE_parse_boot_argn("-panic_on_exception_triage", &tmp, sizeof(tmp))) {
150  		panic_on_exception_triage = true;
151  	}
152  }
153  
154  /*
155   *	Routine:	exception_deliver
156   *	Purpose:
157   *		Make an upcall to the exception server provided.
158   *	Conditions:
159   *		Nothing locked and no resources held.
160   *		Called from an exception context, so
161   *		thread_exception_return and thread_kdb_return
162   *		are possible.
163   *	Returns:
164   *		KERN_SUCCESS if the exception was handled
165   */
166  kern_return_t
167  exception_deliver(
168  	thread_t                thread,
169  	exception_type_t        exception,
170  	mach_exception_data_t   code,
171  	mach_msg_type_number_t  codeCnt,
172  	struct exception_action *excp,
173  	lck_mtx_t                       *mutex)
174  {
175  	ipc_port_t              exc_port = IPC_PORT_NULL;
176  	exception_data_type_t   small_code[EXCEPTION_CODE_MAX];
177  	int                     code64;
178  	int                     behavior;
179  	int                     flavor;
180  	kern_return_t           kr;
181  	task_t task;
182  	ipc_port_t thread_port = IPC_PORT_NULL, task_port = IPC_PORT_NULL;
183  
184  	/*
185  	 *  Save work if we are terminating.
186  	 *  Just go back to our AST handler.
187  	 */
188  	if (!thread->active && !thread->inspection) {
189  		return KERN_SUCCESS;
190  	}
191  
192  	/*
193  	 * If there are no exception actions defined for this entity,
194  	 * we can't deliver here.
195  	 */
196  	if (excp == NULL) {
197  		return KERN_FAILURE;
198  	}
199  
200  	assert(exception < EXC_TYPES_COUNT);
201  	if (exception >= EXC_TYPES_COUNT) {
202  		return KERN_FAILURE;
203  	}
204  
205  	excp = &excp[exception];
206  
207  	/*
208  	 * Snapshot the exception action data under lock for consistency.
209  	 * Hold a reference to the port over the exception_raise_* calls
210  	 * so it can't be destroyed.  This seems like overkill, but keeps
211  	 * the port from disappearing between now and when
212  	 * ipc_object_copyin_from_kernel is finally called.
213  	 */
214  	lck_mtx_lock(mutex);
215  	exc_port = excp->port;
216  	if (!IP_VALID(exc_port)) {
217  		lck_mtx_unlock(mutex);
218  		return KERN_FAILURE;
219  	}
220  	ip_lock(exc_port);
221  	if (!ip_active(exc_port)) {
222  		ip_unlock(exc_port);
223  		lck_mtx_unlock(mutex);
224  		return KERN_FAILURE;
225  	}
226  	ip_reference(exc_port);
227  	exc_port->ip_srights++;
228  	ip_unlock(exc_port);
229  
230  	flavor = excp->flavor;
231  	behavior = excp->behavior;
232  	lck_mtx_unlock(mutex);
233  
234  	code64 = (behavior & MACH_EXCEPTION_CODES);
235  	behavior &= ~MACH_EXCEPTION_MASK;
236  
237  	if (!code64) {
238  		small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]);
239  		small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]);
240  	}
241  
242  	task = thread->task;
243  
244  #if CONFIG_MACF
245  	/* Now is a reasonably good time to check if the exception action is
246  	 * permitted for this process, because after this point we will send
247  	 * the message out almost certainly.
248  	 * As with other failures, exception_triage_thread will go on
249  	 * to the next level.
250  	 */
251  
252  	/* The global exception-to-signal translation port is safe to be an exception handler. */
253  	if (is_ux_handler_port(exc_port) == FALSE &&
254  	    mac_exc_action_check_exception_send(task, excp) != 0) {
255  		kr = KERN_FAILURE;
256  		goto out_release_right;
257  	}
258  #endif
259  
260  	if (behavior != EXCEPTION_STATE) {
261  		task_reference(task);
262  		task_port = convert_task_to_port(task);
263  		/* task ref consumed */
264  		thread_reference(thread);
265  		thread_port = convert_thread_to_port(thread);
266  		/* thread ref consumed */
267  	}
268  
269  	switch (behavior) {
270  	case EXCEPTION_STATE: {
271  		mach_msg_type_number_t state_cnt;
272  		thread_state_data_t state;
273  
274  		c_thr_exc_raise_state++;
275  		state_cnt = _MachineStateCount[flavor];
276  		kr = thread_getstatus_to_user(thread, flavor,
277  		    (thread_state_t)state,
278  		    &state_cnt);
279  		if (kr == KERN_SUCCESS) {
280  			if (code64) {
281  				kr = mach_exception_raise_state(exc_port,
282  				    exception,
283  				    code,
284  				    codeCnt,
285  				    &flavor,
286  				    state, state_cnt,
287  				    state, &state_cnt);
288  			} else {
289  				kr = exception_raise_state(exc_port, exception,
290  				    small_code,
291  				    codeCnt,
292  				    &flavor,
293  				    state, state_cnt,
294  				    state, &state_cnt);
295  			}
296  			if (kr == KERN_SUCCESS) {
297  				if (exception != EXC_CORPSE_NOTIFY) {
298  					kr = thread_setstatus_from_user(thread, flavor,
299  					    (thread_state_t)state,
300  					    state_cnt);
301  				}
302  				goto out_release_right;
303  			}
304  		}
305  
306  		goto out_release_right;
307  	}
308  
309  	case EXCEPTION_DEFAULT:
310  		c_thr_exc_raise++;
311  		if (code64) {
312  			kr = mach_exception_raise(exc_port,
313  			    thread_port,
314  			    task_port,
315  			    exception,
316  			    code,
317  			    codeCnt);
318  		} else {
319  			kr = exception_raise(exc_port,
320  			    thread_port,
321  			    task_port,
322  			    exception,
323  			    small_code,
324  			    codeCnt);
325  		}
326  
327  		goto out_release_right;
328  
329  	case EXCEPTION_STATE_IDENTITY: {
330  		mach_msg_type_number_t state_cnt;
331  		thread_state_data_t state;
332  
333  		c_thr_exc_raise_state_id++;
334  		state_cnt = _MachineStateCount[flavor];
335  		kr = thread_getstatus_to_user(thread, flavor,
336  		    (thread_state_t)state,
337  		    &state_cnt);
338  		if (kr == KERN_SUCCESS) {
339  			if (code64) {
340  				kr = mach_exception_raise_state_identity(
341  					exc_port,
342  					thread_port,
343  					task_port,
344  					exception,
345  					code,
346  					codeCnt,
347  					&flavor,
348  					state, state_cnt,
349  					state, &state_cnt);
350  			} else {
351  				kr = exception_raise_state_identity(exc_port,
352  				    thread_port,
353  				    task_port,
354  				    exception,
355  				    small_code,
356  				    codeCnt,
357  				    &flavor,
358  				    state, state_cnt,
359  				    state, &state_cnt);
360  			}
361  
362  			if (kr == KERN_SUCCESS) {
363  				if (exception != EXC_CORPSE_NOTIFY) {
364  					kr = thread_setstatus_from_user(thread, flavor,
365  					    (thread_state_t)state,
366  					    state_cnt);
367  				}
368  				goto out_release_right;
369  			}
370  		}
371  
372  		goto out_release_right;
373  	}
374  
375  	default:
376  		panic("bad exception behavior!");
377  		return KERN_FAILURE;
378  	}/* switch */
379  
380  out_release_right:
381  
382  	if (task_port) {
383  		ipc_port_release_send(task_port);
384  	}
385  
386  	if (thread_port) {
387  		ipc_port_release_send(thread_port);
388  	}
389  
390  	if (exc_port) {
391  		ipc_port_release_send(exc_port);
392  	}
393  
394  	return kr;
395  }
396  
397  /*
398   * Routine: check_exc_receiver_dependency
399   * Purpose:
400   *      Verify that the port destined for receiving this exception is not
401   *      on the current task. This would cause hang in kernel for
402   *      EXC_CRASH primarily. Note: If port is transferred
403   *      between check and delivery then deadlock may happen.
404   *
405   * Conditions:
406   *		Nothing locked and no resources held.
407   *		Called from an exception context.
408   * Returns:
409   *      KERN_SUCCESS if its ok to send exception message.
410   */
411  kern_return_t
412  check_exc_receiver_dependency(
413  	exception_type_t exception,
414  	struct exception_action *excp,
415  	lck_mtx_t *mutex)
416  {
417  	kern_return_t retval = KERN_SUCCESS;
418  
419  	if (excp == NULL || exception != EXC_CRASH) {
420  		return retval;
421  	}
422  
423  	task_t task = current_task();
424  	lck_mtx_lock(mutex);
425  	ipc_port_t xport = excp[exception].port;
426  	if (IP_VALID(xport)
427  	    && ip_active(xport)
428  	    && task->itk_space == xport->ip_receiver) {
429  		retval = KERN_FAILURE;
430  	}
431  	lck_mtx_unlock(mutex);
432  	return retval;
433  }
434  
435  
436  /*
437   *	Routine:	exception_triage_thread
438   *	Purpose:
439   *		The thread caught an exception.
440   *		We make an up-call to the thread's exception server.
441   *	Conditions:
442   *		Nothing locked and no resources held.
443   *		Called from an exception context, so
444   *		thread_exception_return and thread_kdb_return
445   *		are possible.
446   *	Returns:
447   *		KERN_SUCCESS if exception is handled by any of the handlers.
448   */
449  kern_return_t
450  exception_triage_thread(
451  	exception_type_t        exception,
452  	mach_exception_data_t   code,
453  	mach_msg_type_number_t  codeCnt,
454  	thread_t                thread)
455  {
456  	task_t                  task;
457  	host_priv_t             host_priv;
458  	lck_mtx_t               *mutex;
459  	kern_return_t   kr = KERN_FAILURE;
460  
461  
462  	assert(exception != EXC_RPC_ALERT);
463  
464  	/*
465  	 * If this behavior has been requested by the the kernel
466  	 * (due to the boot environment), we should panic if we
467  	 * enter this function.  This is intended as a debugging
468  	 * aid; it should allow us to debug why we caught an
469  	 * exception in environments where debugging is especially
470  	 * difficult.
471  	 */
472  	if (panic_on_exception_triage) {
473  		panic("called exception_triage when it was forbidden by the boot environment");
474  	}
475  
476  	/*
477  	 * Try to raise the exception at the activation level.
478  	 */
479  	mutex = &thread->mutex;
480  	if (KERN_SUCCESS == check_exc_receiver_dependency(exception, thread->exc_actions, mutex)) {
481  		kr = exception_deliver(thread, exception, code, codeCnt, thread->exc_actions, mutex);
482  		if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
483  			goto out;
484  		}
485  	}
486  
487  	/*
488  	 * Maybe the task level will handle it.
489  	 */
490  	task = thread->task;
491  	mutex = &task->itk_lock_data;
492  	if (KERN_SUCCESS == check_exc_receiver_dependency(exception, task->exc_actions, mutex)) {
493  		kr = exception_deliver(thread, exception, code, codeCnt, task->exc_actions, mutex);
494  		if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
495  			goto out;
496  		}
497  	}
498  
499  	/*
500  	 * How about at the host level?
501  	 */
502  	host_priv = host_priv_self();
503  	mutex = &host_priv->lock;
504  
505  	if (KERN_SUCCESS == check_exc_receiver_dependency(exception, host_priv->exc_actions, mutex)) {
506  		kr = exception_deliver(thread, exception, code, codeCnt, host_priv->exc_actions, mutex);
507  		if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
508  			goto out;
509  		}
510  	}
511  
512  out:
513  #ifndef __DARLING__
514  	if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) &&
515  	    (exception != EXC_GUARD) && (exception != EXC_CORPSE_NOTIFY)) {
516  		thread_exception_return();
517  	}
518  #endif // __DARLING__
519  	return kr;
520  }
521  
522  /*
523   *	Routine:	exception_triage
524   *	Purpose:
525   *		The current thread caught an exception.
526   *		We make an up-call to the thread's exception server.
527   *	Conditions:
528   *		Nothing locked and no resources held.
529   *		Called from an exception context, so
530   *		thread_exception_return and thread_kdb_return
531   *		are possible.
532   *	Returns:
533   *		KERN_SUCCESS if exception is handled by any of the handlers.
534   */
535  int debug4k_panic_on_exception = 0;
536  kern_return_t
537  exception_triage(
538  	exception_type_t        exception,
539  	mach_exception_data_t   code,
540  	mach_msg_type_number_t  codeCnt)
541  {
542  	thread_t thread = current_thread();
543  	if (VM_MAP_PAGE_SIZE(thread->task->map) < PAGE_SIZE) {
544  		DEBUG4K_EXC("thread %p task %p map %p exception %d codes 0x%llx 0x%llx \n", thread, thread->task, thread->task->map, exception, code[0], code[1]);
545  		if (debug4k_panic_on_exception) {
546  			panic("DEBUG4K %s:%d thread %p task %p map %p exception %d codes 0x%llx 0x%llx \n", __FUNCTION__, __LINE__, thread, thread->task, thread->task->map, exception, code[0], code[1]);
547  		}
548  	}
549  #if __has_feature(ptrauth_calls)
550  	/*
551  	 * If it is a ptrauth violation, then check if the task has the TF_PAC_EXC_FATAL
552  	 * flag set and isn't being ptraced. If so, terminate the task via exit_with_reason
553  	 */
554  	if (exception & EXC_PTRAUTH_BIT) {
555  		exception &= ~EXC_PTRAUTH_BIT;
556  
557  		boolean_t traced_flag = FALSE;
558  		task_t task = thread->task;
559  		void *proc = task->bsd_info;
560  
561  		if (task->bsd_info) {
562  			traced_flag = proc_is_traced(proc);
563  		}
564  
565  		if (task_is_pac_exception_fatal(current_task()) && !traced_flag) {
566  			exit_with_pac_exception(proc, exception, code[0], code[1]);
567  			thread_exception_return();
568  			/* NOT_REACHABLE */
569  		}
570  	}
571  #endif /* __has_feature(ptrauth_calls) */
572  	return exception_triage_thread(exception, code, codeCnt, thread);
573  }
574  
575  kern_return_t
576  bsd_exception(
577  	exception_type_t        exception,
578  	mach_exception_data_t   code,
579  	mach_msg_type_number_t  codeCnt)
580  {
581  	task_t                  task;
582  	lck_mtx_t               *mutex;
583  	thread_t                self = current_thread();
584  	kern_return_t           kr;
585  
586  	/*
587  	 * Maybe the task level will handle it.
588  	 */
589  	task = current_task();
590  	mutex = &task->itk_lock_data;
591  
592  	kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex);
593  
594  	if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
595  		return KERN_SUCCESS;
596  	}
597  	return KERN_FAILURE;
598  }
599  
600  
601  /*
602   * Raise an exception on a task.
603   * This should tell launchd to launch Crash Reporter for this task.
604   */
605  kern_return_t
606  task_exception_notify(exception_type_t exception,
607      mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode)
608  {
609  	mach_exception_data_type_t      code[EXCEPTION_CODE_MAX];
610  	wait_interrupt_t                wsave;
611  	kern_return_t kr = KERN_SUCCESS;
612  
613  	code[0] = exccode;
614  	code[1] = excsubcode;
615  
616  	wsave = thread_interrupt_level(THREAD_UNINT);
617  	kr = exception_triage(exception, code, EXCEPTION_CODE_MAX);
618  	(void) thread_interrupt_level(wsave);
619  	return kr;
620  }
621  
622  
623  /*
624   *	Handle interface for special performance monitoring
625   *	This is a special case of the host exception handler
626   */
627  kern_return_t
628  sys_perf_notify(thread_t thread, int pid)
629  {
630  	host_priv_t             hostp;
631  	ipc_port_t              xport;
632  	wait_interrupt_t        wsave;
633  	kern_return_t           ret;
634  
635  	hostp = host_priv_self();       /* Get the host privileged ports */
636  	mach_exception_data_type_t      code[EXCEPTION_CODE_MAX];
637  	code[0] = 0xFF000001;           /* Set terminate code */
638  	code[1] = pid;          /* Pass out the pid */
639  
640  	struct task *task = thread->task;
641  	xport = hostp->exc_actions[EXC_RPC_ALERT].port;
642  
643  	/* Make sure we're not catching our own exception */
644  	if (!IP_VALID(xport) ||
645  	    !ip_active(xport) ||
646  	    task->itk_space == xport->data.receiver) {
647  		return KERN_FAILURE;
648  	}
649  
650  	wsave = thread_interrupt_level(THREAD_UNINT);
651  	ret = exception_deliver(
652  		thread,
653  		EXC_RPC_ALERT,
654  		code,
655  		2,
656  		hostp->exc_actions,
657  		&hostp->lock);
658  	(void)thread_interrupt_level(wsave);
659  
660  	return ret;
661  }