Exception-port registration and restoration

osfmk/kern/ipc_tt.c · 4983 lines · browse source

Validation and ownership of task/thread exception-port registrations. Getters return masks, ports, behaviors and flavors; swap operations return previous settings while installing replacements. The source includes configuration-dependent policy checks and send-right copying and release.

set_exception_ports_validation source
Routine: set_exception_ports_validation Purpose: Common argument validation shared between all exception port setting/swapping routines Conditions: Nothing locked. Returns: KERN_SUCCESS Setting the exception port is allowed with these arguments KERN_INVALID_ARGUMENT Invalid arguments KERN_INVALID_RIGHT Incorrect port configuration KERN_DENIED Denied by security policy
kern_return_t
set_exception_ports_validation(
	task_t                  task,
	exception_mask_t        exception_mask,
	ipc_port_t              new_port,
	exception_behavior_t    new_behavior,
	thread_state_flavor_t   new_flavor,
	bool                    hardened_exception)
{
	if (exception_mask & ~EXC_MASK_VALID) {
		return KERN_INVALID_ARGUMENT;
	}

	if (IP_VALID(new_port)) {
		switch (new_behavior & ~MACH_EXCEPTION_MASK) {
		case EXCEPTION_DEFAULT:
		case EXCEPTION_STATE:
		case EXCEPTION_STATE_IDENTITY:
		case EXCEPTION_IDENTITY_PROTECTED:
		case EXCEPTION_STATE_IDENTITY_PROTECTED:
			break;

		default:
			return KERN_INVALID_ARGUMENT;
		}
	}

	if (IP_VALID(new_port) && !ipc_is_valid_exception_port(task, new_port)) {
		return KERN_INVALID_RIGHT;
	}
… more in source
thread_set_exception_ports_internal source
Routine: thread_set_exception_ports_internal Purpose: Set a new exception action on the thread Conditions: Arguments have been validated via `set_exception_ports_validation` Nothing locked. Returns: KERN_SUCCESS Setting the exception port is allowed with these arguments KERN_FAILURE Thread is inactive
kern_return_t
thread_set_exception_ports_internal(
	thread_t                thread,
	exception_mask_t        exception_mask,
	ipc_port_t              new_port,
	exception_behavior_t    new_behavior,
	thread_state_flavor_t   new_flavor,
	boolean_t               hardened)
{
	ipc_port_t  old_port[EXC_TYPES_COUNT];
	thread_ro_t tro;
	boolean_t   privileged = task_is_privileged(current_task());

#if CONFIG_MACF
	if (mac_task_check_set_thread_exception_ports(current_task(), get_threadtask(thread), exception_mask, new_behavior) != 0) {
		return KERN_NO_ACCESS;
	}

	struct label *new_label = mac_exc_create_label_for_current_proc();
#endif

	tro = get_thread_ro(thread);
	thread_mtx_lock(thread);

	if (!thread->active) {
		thread_mtx_unlock(thread);
#if CONFIG_MACF
		mac_exc_free_label(new_label);
#endif
		return KERN_FAILURE;
… more in source
thread_set_exception_ports source · thread_set_exception_ports reference
Routine: thread/task_set_exception_ports [kernel call] Purpose: Sets the thread/task exception port, flavor and behavior for the exception types specified by the mask. There will be one send right per exception per valid port. Conditions: Nothing locked. If successful, consumes the supplied send right. Returns: KERN_SUCCESS Changed the special port. KERN_INVALID_ARGUMENT The thread is null, Illegal mask bit set. Illegal exception behavior KERN_FAILURE The thread is dead. KERN_NO_ACCESS Restricted access to set port
kern_return_t
thread_set_exception_ports(
	thread_t                thread,
	exception_mask_t        exception_mask,
	ipc_port_t              new_port,
	exception_behavior_t    new_behavior,
	thread_state_flavor_t   new_flavor)
{
	if (thread == THREAD_NULL) {
		return KERN_INVALID_ARGUMENT;
	}
	bool hardened_exception_flow = false;
	kern_return_t kr = set_exception_ports_validation(get_threadtask(thread),
	    exception_mask, new_port, new_behavior, new_flavor, hardened_exception_flow);
	if (kr != KERN_SUCCESS) {
		return kr;
	}

	return thread_set_exception_ports_internal(thread, exception_mask, new_port, new_behavior, new_flavor, false);
}
task_set_exception_ports source · task_set_exception_ports reference
kern_return_t
task_set_exception_ports(
	task_t                                  task,
	exception_mask_t                exception_mask,
	ipc_port_t                              new_port,
	exception_behavior_t    new_behavior,
	thread_state_flavor_t   new_flavor)
{
	ipc_port_t              old_port[EXC_TYPES_COUNT];
	boolean_t privileged = task_is_privileged(current_task());
	register int    i;

	if (task == TASK_NULL) {
		return KERN_INVALID_ARGUMENT;
	}
	bool hardened_exception_flow = false;
	kern_return_t kr = set_exception_ports_validation(task, exception_mask,
	    new_port, new_behavior, new_flavor, hardened_exception_flow);
	if (kr != KERN_SUCCESS) {
		return kr;
	}


#if CONFIG_MACF
	if (mac_task_check_set_task_exception_ports(current_task(), task, exception_mask, new_behavior) != 0) {
		return KERN_NO_ACCESS;
	}

	struct label *new_label = mac_exc_create_label_for_current_proc();
#endif
… more in source
thread_swap_exception_ports source · thread_swap_exception_ports reference
Routine: thread/task_swap_exception_ports [kernel call] Purpose: Sets the thread/task exception port, flavor and behavior for the exception types specified by the mask. The old ports, behavior and flavors are returned Count specifies the array sizes on input and the number of returned ports etc. on output. The arrays must be large enough to hold all the returned data, MIG returnes an error otherwise. The masks array specifies the corresponding exception type(s). Conditions: Nothing locked. If successful, consumes the supplied send right. Returns upto [in} CountCnt elements. Returns: KERN_SUCCESS Changed the special port. KERN_INVALID_ARGUMENT The thread is null, Illegal mask bit set. Illegal exception behavior KERN_FAILURE The thread is dead. KERN_NO_ACCESS Restricted access to set port
kern_return_t
thread_swap_exception_ports(
	thread_t                        thread,
	exception_mask_t                exception_mask,
	ipc_port_t                      new_port,
	exception_behavior_t            new_behavior,
	thread_state_flavor_t           new_flavor,
	exception_mask_array_t          masks,
	mach_msg_type_number_t          *CountCnt,
	exception_port_array_t          ports,
	exception_behavior_array_t      behaviors,
	thread_state_flavor_array_t     flavors)
{
	ipc_port_t  old_port[EXC_TYPES_COUNT];
	thread_ro_t tro;
	boolean_t   privileged = task_is_privileged(current_task());
	unsigned int    i, j, count;

	if (thread == THREAD_NULL) {
		return KERN_INVALID_ARGUMENT;
	}
	bool hardened_exception_flow = false;
	kern_return_t kr = set_exception_ports_validation(get_threadtask(thread),
	    exception_mask, new_port, new_behavior, new_flavor, hardened_exception_flow);
	if (kr != KERN_SUCCESS) {
		return kr;
	}

#if CONFIG_MACF
	if (mac_task_check_set_thread_exception_ports(current_task(), get_threadtask(thread), exception_mask, new_behavior) != 0) {
… more in source
task_swap_exception_ports source · task_swap_exception_ports reference
kern_return_t
task_swap_exception_ports(
	task_t                                          task,
	exception_mask_t                        exception_mask,
	ipc_port_t                                      new_port,
	exception_behavior_t            new_behavior,
	thread_state_flavor_t           new_flavor,
	exception_mask_array_t          masks,
	mach_msg_type_number_t          *CountCnt,
	exception_port_array_t          ports,
	exception_behavior_array_t      behaviors,
	thread_state_flavor_array_t     flavors)
{
	ipc_port_t              old_port[EXC_TYPES_COUNT];
	boolean_t privileged = task_is_privileged(current_task());
	unsigned int    i, j, count;

#if CONFIG_MACF
	struct label *new_label;
#endif

	if (task == TASK_NULL) {
		return KERN_INVALID_ARGUMENT;
	}
	bool hardened_exception_flow = false;
	kern_return_t kr = set_exception_ports_validation(task, exception_mask,
	    new_port, new_behavior, new_flavor, hardened_exception_flow);
	if (kr != KERN_SUCCESS) {
		return kr;
	}
… more in source
thread_get_exception_ports_internal source
Routine: thread/task_get_exception_ports [kernel call] Purpose: Clones a send right for each of the thread/task's exception ports specified in the mask and returns the behaviour and flavor of said port. Returns upto [in} CountCnt elements. Conditions: Nothing locked. Returns: KERN_SUCCESS Extracted a send right. KERN_INVALID_ARGUMENT The thread is null, Invalid special port, Illegal mask bit set. KERN_FAILURE The thread is dead.
static kern_return_t
thread_get_exception_ports_internal(
	thread_t                        thread,
	exception_mask_t                exception_mask,
	exception_mask_array_t          masks,
	mach_msg_type_number_t          *CountCnt,
	exception_port_info_array_t     ports_info,
	exception_port_array_t          ports,
	exception_behavior_array_t      behaviors,
	thread_state_flavor_array_t     flavors)
{
	unsigned int count;
	boolean_t info_only = (ports_info != NULL);
	thread_ro_t tro;
	ipc_port_t port_ptrs[EXC_TYPES_COUNT]; /* pointers only, does not hold right */

	if (thread == THREAD_NULL) {
		return KERN_INVALID_ARGUMENT;
	}

	if (exception_mask & ~EXC_MASK_VALID) {
		return KERN_INVALID_ARGUMENT;
	}

	if (!info_only && !ports) {
		return KERN_INVALID_ARGUMENT;
	}

	/*
	 * Allocate a save area for FP state before taking thread lock,
… more in source
task_get_exception_ports_internal source
static kern_return_t
task_get_exception_ports_internal(
	task_t                          task,
	exception_mask_t                exception_mask,
	exception_mask_array_t          masks,
	mach_msg_type_number_t          *CountCnt,
	exception_port_info_array_t     ports_info,
	exception_port_array_t          ports,
	exception_behavior_array_t      behaviors,
	thread_state_flavor_array_t     flavors)
{
	unsigned int count;
	boolean_t info_only = (ports_info != NULL);
	ipc_port_t port_ptrs[EXC_TYPES_COUNT]; /* pointers only, does not hold right */

	if (task == TASK_NULL) {
		return KERN_INVALID_ARGUMENT;
	}

	if (exception_mask & ~EXC_MASK_VALID) {
		return KERN_INVALID_ARGUMENT;
	}

	if (!info_only && !ports) {
		return KERN_INVALID_ARGUMENT;
	}

	/*
	 * Allocate a save area for FP state before taking task lock,
	 * if necessary, to ensure that VM_KERNEL_ADDRHASH() doesn't cause
… more in source