/*
- * Copyright (c) 2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2020 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <kern/kalloc.h>
#include <arm/vmparam.h>
#include <arm/cpu_data_internal.h>
+#include <arm/misc_protos.h>
#include <arm64/proc_reg.h>
#if __has_feature(ptrauth_calls)
#include <ptrauth.h>
#endif
+
struct arm_vfpv2_state {
__uint32_t __r[32];
__uint32_t __fpscr;
*/
void thread_set_child(thread_t child, int pid);
void thread_set_parent(thread_t parent, int pid);
+static void free_debug_state(thread_t thread);
/*
* Maps state flavor to number of words in the state:
arm_saved_state_t * saved_state)
{
uint32_t i;
+#if __has_feature(ptrauth_calls)
+ boolean_t intr = ml_set_interrupts_enabled(FALSE);
+#endif /* __has_feature(ptrauth_calls) */
assert(is_saved_state64(saved_state));
+#if __has_feature(ptrauth_calls)
+ MANIPULATE_SIGNED_THREAD_STATE(saved_state,
+ "and w2, w2, %w[not_psr64_user_mask] \n"
+ "mov w6, %w[cpsr] \n"
+ "and w6, w6, %w[psr64_user_mask] \n"
+ "orr w2, w2, w6 \n"
+ "str w2, [x0, %[SS64_CPSR]] \n",
+ [cpsr] "r"(ts64->cpsr),
+ [psr64_user_mask] "i"(PSR64_USER_MASK),
+ [not_psr64_user_mask] "i"(~PSR64_USER_MASK)
+ );
+ /*
+ * Make writes to ts64->cpsr visible first, since it's useful as a
+ * canary to detect thread-state corruption.
+ */
+ __builtin_arm_dmb(DMB_ST);
+#else
+ set_saved_state_cpsr(saved_state,
+ (get_saved_state_cpsr(saved_state) & ~PSR64_USER_MASK) | (ts64->cpsr & PSR64_USER_MASK));
+#endif /* __has_feature(ptrauth_calls) */
set_saved_state_fp(saved_state, ts64->fp);
set_saved_state_lr(saved_state, ts64->lr);
set_saved_state_sp(saved_state, ts64->sp);
set_saved_state_pc(saved_state, ts64->pc);
- set_saved_state_cpsr(saved_state, (ts64->cpsr & ~PSR64_MODE_MASK) | PSR64_MODE_RW_64);
for (i = 0; i < 29; i++) {
set_saved_state_reg(saved_state, i, ts64->x[i]);
}
+
+#if __has_feature(ptrauth_calls)
+ ml_set_interrupts_enabled(intr);
+#endif /* __has_feature(ptrauth_calls) */
}
#endif /* __arm64__ */
// Note that kernel threads never have disable_user_jop set
if (current_thread()->machine.disable_user_jop || !thread_is_64bit_addr(current_thread()) ||
- thread->machine.disable_user_jop || !thread_is_64bit_addr(thread) ||
- (BootArgs->bootFlags & kBootFlagsDisableUserThreadStateJOP)) {
+ thread->machine.disable_user_jop || !thread_is_64bit_addr(thread)
+ ) {
ts64->flags = __DARWIN_ARM_THREAD_STATE64_FLAGS_NO_PTRAUTH;
return KERN_SUCCESS;
}
// will round-trip correctly even if IA-signed again below (and IA-authd later)
}
- if (BootArgs->bootFlags & kBootFlagsDisableUserJOP) {
+ if (arm_user_jop_disabled()) {
return KERN_SUCCESS;
}
if (ts64->pc) {
ts64->pc = (uintptr_t)pmap_sign_user_ptr((void*)ts64->pc,
- ptrauth_key_process_independent_code, ptrauth_string_discriminator("pc"));
+ ptrauth_key_process_independent_code, ptrauth_string_discriminator("pc"),
+ thread->machine.jop_pid);
}
if (ts64->lr && !(ts64->flags & __DARWIN_ARM_THREAD_STATE64_FLAGS_IB_SIGNED_LR)) {
ts64->lr = (uintptr_t)pmap_sign_user_ptr((void*)ts64->lr,
- ptrauth_key_process_independent_code, ptrauth_string_discriminator("lr"));
+ ptrauth_key_process_independent_code, ptrauth_string_discriminator("lr"),
+ thread->machine.jop_pid);
}
if (ts64->sp) {
ts64->sp = (uintptr_t)pmap_sign_user_ptr((void*)ts64->sp,
- ptrauth_key_process_independent_data, ptrauth_string_discriminator("sp"));
+ ptrauth_key_process_independent_data, ptrauth_string_discriminator("sp"),
+ thread->machine.jop_pid);
}
if (ts64->fp) {
ts64->fp = (uintptr_t)pmap_sign_user_ptr((void*)ts64->fp,
- ptrauth_key_process_independent_data, ptrauth_string_discriminator("fp"));
+ ptrauth_key_process_independent_data, ptrauth_string_discriminator("fp"),
+ thread->machine.jop_pid);
}
return KERN_SUCCESS;
}
if (ts64->flags & __DARWIN_ARM_THREAD_STATE64_FLAGS_NO_PTRAUTH) {
- if (thread->machine.disable_user_jop || !thread_is_64bit_addr(thread) ||
- (BootArgs->bootFlags & kBootFlagsDisableUserThreadStateJOP)) {
+ if (thread->machine.disable_user_jop || !thread_is_64bit_addr(thread)
+ ) {
return KERN_SUCCESS;
}
// Disallow setting unsigned thread state on JOP-enabled processes.
// correctly below.
}
- if (BootArgs->bootFlags & kBootFlagsDisableUserJOP) {
+ if (arm_user_jop_disabled()) {
return KERN_SUCCESS;
}
if (ts64->pc) {
ts64->pc = (uintptr_t)pmap_auth_user_ptr((void*)ts64->pc,
- ptrauth_key_process_independent_code, ptrauth_string_discriminator("pc"));
+ ptrauth_key_process_independent_code, ptrauth_string_discriminator("pc"),
+ thread->machine.jop_pid);
}
if (ts64->lr && !(ts64->flags & __DARWIN_ARM_THREAD_STATE64_FLAGS_IB_SIGNED_LR)) {
ts64->lr = (uintptr_t)pmap_auth_user_ptr((void*)ts64->lr,
- ptrauth_key_process_independent_code, ptrauth_string_discriminator("lr"));
+ ptrauth_key_process_independent_code, ptrauth_string_discriminator("lr"),
+ thread->machine.jop_pid);
}
if (ts64->sp) {
ts64->sp = (uintptr_t)pmap_auth_user_ptr((void*)ts64->sp,
- ptrauth_key_process_independent_data, ptrauth_string_discriminator("sp"));
+ ptrauth_key_process_independent_data, ptrauth_string_discriminator("sp"),
+ thread->machine.jop_pid);
}
if (ts64->fp) {
ts64->fp = (uintptr_t)pmap_auth_user_ptr((void*)ts64->fp,
- ptrauth_key_process_independent_data, ptrauth_string_discriminator("fp"));
+ ptrauth_key_process_independent_data, ptrauth_string_discriminator("fp"),
+ thread->machine.jop_pid);
}
return KERN_SUCCESS;
kern_return_t
machine_thread_siguctx_pointer_convert_to_user(
- __assert_only thread_t thread,
+ thread_t thread,
user_addr_t *uctxp)
{
#if __has_feature(ptrauth_calls)
return KERN_SUCCESS;
}
- if (BootArgs->bootFlags & kBootFlagsDisableUserJOP) {
+ if (arm_user_jop_disabled()) {
return KERN_SUCCESS;
}
if (*uctxp) {
*uctxp = (uintptr_t)pmap_sign_user_ptr((void*)*uctxp,
- ptrauth_key_process_independent_data, ptrauth_string_discriminator("uctx"));
+ ptrauth_key_process_independent_data, ptrauth_string_discriminator("uctx"),
+ thread->machine.jop_pid);
}
return KERN_SUCCESS;
kern_return_t
machine_thread_function_pointers_convert_from_user(
- __assert_only thread_t thread,
+ thread_t thread,
user_addr_t *fptrs,
uint32_t count)
{
return KERN_SUCCESS;
}
- if (BootArgs->bootFlags & kBootFlagsDisableUserJOP) {
+ if (arm_user_jop_disabled()) {
return KERN_SUCCESS;
}
while (count--) {
if (*fptrs) {
*fptrs = (uintptr_t)pmap_auth_user_ptr((void*)*fptrs,
- ptrauth_key_function_pointer, 0);
+ ptrauth_key_function_pointer, 0, thread->machine.jop_pid);
}
fptrs++;
}
return KERN_INVALID_ARGUMENT;
}
- kern_return_t rn = handle_get_arm64_thread_state(tstate, count, thread->machine.upcb);
+ const arm_saved_state_t *current_state = thread->machine.upcb;
+
+ kern_return_t rn = handle_get_arm64_thread_state(tstate, count,
+ current_state);
if (rn) {
return rn;
}
+
break;
}
#endif
assert(sizeof(*state) == sizeof(*thread_state));
bcopy(thread_state, state, sizeof(arm_neon_state64_t));
+
*count = ARM_NEON_STATE64_COUNT;
break;
}
return KERN_INVALID_ARGUMENT;
}
+
rn = handle_set_arm64_thread_state(tstate, count, thread->machine.upcb);
if (rn) {
return rn;
}
if (!enabled) {
- arm_debug_state32_t *thread_state = find_debug_state32(thread);
- if (thread_state != NULL) {
- void *pTmp = thread->machine.DebugData;
- thread->machine.DebugData = NULL;
- zfree(ads_zone, pTmp);
- }
+ free_debug_state(thread);
} else {
- arm_debug_state32_t *thread_state = find_debug_state32(thread);
+ arm_debug_state32_t *thread_state = find_or_allocate_debug_state32(thread);
+
if (thread_state == NULL) {
- thread->machine.DebugData = zalloc(ads_zone);
- bzero(thread->machine.DebugData, sizeof *(thread->machine.DebugData));
- thread->machine.DebugData->dsh.flavor = ARM_DEBUG_STATE32;
- thread->machine.DebugData->dsh.count = ARM_DEBUG_STATE32_COUNT;
- thread_state = find_debug_state32(thread);
+ return KERN_FAILURE;
}
- assert(NULL != thread_state);
for (i = 0; i < 16; i++) {
/* set appropriate privilege; mask out unknown bits */
state = (arm_debug_state32_t *) tstate;
- if (state->mdscr_el1 & 0x1) {
+ if (state->mdscr_el1 & MDSCR_SS) {
enabled = TRUE;
}
}
if (!enabled) {
- arm_debug_state32_t *thread_state = find_debug_state32(thread);
- if (thread_state != NULL) {
- void *pTmp = thread->machine.DebugData;
- thread->machine.DebugData = NULL;
- zfree(ads_zone, pTmp);
- }
+ free_debug_state(thread);
} else {
- arm_debug_state32_t *thread_state = find_debug_state32(thread);
+ arm_debug_state32_t * thread_state = find_or_allocate_debug_state32(thread);
+
if (thread_state == NULL) {
- thread->machine.DebugData = zalloc(ads_zone);
- bzero(thread->machine.DebugData, sizeof *(thread->machine.DebugData));
- thread->machine.DebugData->dsh.flavor = ARM_DEBUG_STATE32;
- thread->machine.DebugData->dsh.count = ARM_DEBUG_STATE32_COUNT;
- thread_state = find_debug_state32(thread);
+ return KERN_FAILURE;
}
- assert(NULL != thread_state);
- if (state->mdscr_el1 & 0x1) {
- thread_state->mdscr_el1 |= 0x1;
+ if (state->mdscr_el1 & MDSCR_SS) {
+ thread_state->mdscr_el1 |= MDSCR_SS;
} else {
- thread_state->mdscr_el1 &= ~0x1;
+ thread_state->mdscr_el1 &= ~MDSCR_SS;
}
for (i = 0; i < 16; i++) {
state = (arm_debug_state64_t *) tstate;
- if (state->mdscr_el1 & 0x1) {
+ if (state->mdscr_el1 & MDSCR_SS) {
enabled = TRUE;
}
}
if (!enabled) {
- arm_debug_state64_t *thread_state = find_debug_state64(thread);
- if (thread_state != NULL) {
- void *pTmp = thread->machine.DebugData;
- thread->machine.DebugData = NULL;
- zfree(ads_zone, pTmp);
- }
+ free_debug_state(thread);
} else {
- arm_debug_state64_t *thread_state = find_debug_state64(thread);
+ arm_debug_state64_t *thread_state = find_or_allocate_debug_state64(thread);
+
if (thread_state == NULL) {
- thread->machine.DebugData = zalloc(ads_zone);
- bzero(thread->machine.DebugData, sizeof *(thread->machine.DebugData));
- thread->machine.DebugData->dsh.flavor = ARM_DEBUG_STATE64;
- thread->machine.DebugData->dsh.count = ARM_DEBUG_STATE64_COUNT;
- thread_state = find_debug_state64(thread);
+ return KERN_FAILURE;
}
- assert(NULL != thread_state);
- if (state->mdscr_el1 & 0x1) {
- thread_state->mdscr_el1 |= 0x1;
+ if (state->mdscr_el1 & MDSCR_SS) {
+ thread_state->mdscr_el1 |= MDSCR_SS;
} else {
- thread_state->mdscr_el1 &= ~0x1;
+ thread_state->mdscr_el1 &= ~MDSCR_SS;
}
for (i = 0; i < 16; i++) {
assert(sizeof(*state) == sizeof(*thread_state));
bcopy(state, thread_state, sizeof(arm_neon_state64_t));
+
thread->machine.uNeon->nsh.flavor = ARM_NEON_SAVED_STATE64;
thread->machine.uNeon->nsh.count = ARM_NEON_SAVED_STATE64_COUNT;
break;
#if defined(HAS_APPLE_PAC)
/* Sign the initial user-space thread state */
if (thread->machine.upcb != NULL) {
+ boolean_t intr = ml_set_interrupts_enabled(FALSE);
ml_sign_thread_state(thread->machine.upcb, 0, 0, 0, 0, 0);
+ ml_set_interrupts_enabled(intr);
}
#endif /* defined(HAS_APPLE_PAC) */
}
}
+/**
+ * Finds the debug state for the given 64 bit thread, allocating one if it
+ * does not exist.
+ *
+ * @param thread 64 bit thread to find or allocate debug state for
+ *
+ * @returns A pointer to the given thread's 64 bit debug state or a null
+ * pointer if the given thread is null or the allocation of a new
+ * debug state fails.
+ */
+arm_debug_state64_t *
+find_or_allocate_debug_state64(thread_t thread)
+{
+ arm_debug_state64_t *thread_state = find_debug_state64(thread);
+ if (thread != NULL && thread_state == NULL) {
+ thread->machine.DebugData = zalloc(ads_zone);
+ if (thread->machine.DebugData != NULL) {
+ bzero(thread->machine.DebugData, sizeof *(thread->machine.DebugData));
+ thread->machine.DebugData->dsh.flavor = ARM_DEBUG_STATE64;
+ thread->machine.DebugData->dsh.count = ARM_DEBUG_STATE64_COUNT;
+ thread_state = find_debug_state64(thread);
+ }
+ }
+ return thread_state;
+}
+
+/**
+ * Finds the debug state for the given 32 bit thread, allocating one if it
+ * does not exist.
+ *
+ * @param thread 32 bit thread to find or allocate debug state for
+ *
+ * @returns A pointer to the given thread's 32 bit debug state or a null
+ * pointer if the given thread is null or the allocation of a new
+ * debug state fails.
+ */
+arm_debug_state32_t *
+find_or_allocate_debug_state32(thread_t thread)
+{
+ arm_debug_state32_t *thread_state = find_debug_state32(thread);
+ if (thread != NULL && thread_state == NULL) {
+ thread->machine.DebugData = zalloc(ads_zone);
+ if (thread->machine.DebugData != NULL) {
+ bzero(thread->machine.DebugData, sizeof *(thread->machine.DebugData));
+ thread->machine.DebugData->dsh.flavor = ARM_DEBUG_STATE32;
+ thread->machine.DebugData->dsh.count = ARM_DEBUG_STATE32_COUNT;
+ thread_state = find_debug_state32(thread);
+ }
+ }
+ return thread_state;
+}
+
+/**
+ * Frees a thread's debug state if allocated. Otherwise does nothing.
+ *
+ * @param thread thread to free the debug state of
+ */
+static inline void
+free_debug_state(thread_t thread)
+{
+ if (thread != NULL && thread->machine.DebugData != NULL) {
+ void *pTmp = thread->machine.DebugData;
+ thread->machine.DebugData = NULL;
+ zfree(ads_zone, pTmp);
+ }
+}
+
/*
* Routine: thread_userstack
*
break;
}
- /* INTENTIONAL FALL THROUGH (see machine_thread_set_state) */
+ /* INTENTIONAL FALL THROUGH (see machine_thread_set_state) */
+ OS_FALLTHROUGH;
case ARM_THREAD_STATE32:
if (count != ARM_THREAD_STATE32_COUNT) {
return KERN_INVALID_ARGUMENT;
* Routine: thread_adjuserstack
*
*/
-uint64_t
+user_addr_t
thread_adjuserstack(thread_t thread,
int adjust)
{
return sp;
}
+
/*
* Routine: thread_setentrypoint
*
thread_entrypoint(__unused thread_t thread,
int flavor,
thread_state_t tstate,
- unsigned int count __unused,
+ unsigned int count,
mach_vm_offset_t * entry_point
)
{
{
struct arm_thread_state *state;
+ if (count != ARM_THREAD_STATE_COUNT) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
state = (struct arm_thread_state *) tstate;
/*
{
struct arm_thread_state64 *state;
+ if (count != ARM_THREAD_STATE64_COUNT) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
state = (struct arm_thread_state64*) tstate;
/*