949 lines
25 KiB
C
949 lines
25 KiB
C
/*
|
|
* Copyright (c) 2000-2007 Apple Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <mach/mach_types.h>
|
|
#include <mach/exception_types.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/proc_internal.h>
|
|
#include <sys/user.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/ucontext.h>
|
|
#include <sys/sysproto.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/ux_exception.h>
|
|
|
|
#include <arm/signal.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/kdebug.h>
|
|
#include <sys/sdt.h>
|
|
#include <sys/wait.h>
|
|
#include <kern/thread.h>
|
|
#include <mach/arm/thread_status.h>
|
|
#include <arm64/proc_reg.h>
|
|
|
|
#include <kern/assert.h>
|
|
#include <kern/ast.h>
|
|
#include <pexpert/pexpert.h>
|
|
#include <sys/random.h>
|
|
|
|
extern struct arm_saved_state *get_user_regs(thread_t);
|
|
extern user_addr_t thread_get_cthread_self(void);
|
|
extern kern_return_t thread_getstatus(thread_t act, int flavor,
|
|
thread_state_t tstate, mach_msg_type_number_t *count);
|
|
extern kern_return_t thread_getstatus_to_user(thread_t act, int flavor,
|
|
thread_state_t tstate, mach_msg_type_number_t *count, thread_set_status_flags_t);
|
|
extern kern_return_t machine_thread_state_convert_to_user(thread_t act, int flavor,
|
|
thread_state_t tstate, mach_msg_type_number_t *count, thread_set_status_flags_t);
|
|
extern kern_return_t thread_setstatus(thread_t thread, int flavor,
|
|
thread_state_t tstate, mach_msg_type_number_t count);
|
|
extern kern_return_t thread_setstatus_from_user(thread_t thread, int flavor,
|
|
thread_state_t tstate, mach_msg_type_number_t count,
|
|
thread_state_t old_tstate, mach_msg_type_number_t old_count,
|
|
thread_set_status_flags_t flags);
|
|
extern task_t current_task(void);
|
|
extern bool task_needs_user_signed_thread_state(task_t);
|
|
/* XXX Put these someplace smarter... */
|
|
typedef struct mcontext32 mcontext32_t;
|
|
typedef struct mcontext64 mcontext64_t;
|
|
|
|
/* Signal handler flavors supported */
|
|
/* These defns should match the libplatform implmn */
|
|
#define UC_TRAD 1
|
|
#define UC_FLAVOR 30
|
|
#define UC_SET_ALT_STACK 0x40000000
|
|
#define UC_RESET_ALT_STACK 0x80000000
|
|
|
|
/* The following are valid mcontext sizes */
|
|
#define UC_FLAVOR_SIZE32 ((ARM_THREAD_STATE_COUNT + ARM_EXCEPTION_STATE_COUNT + ARM_VFP_STATE_COUNT) * sizeof(int))
|
|
#define UC_FLAVOR_SIZE64 ((ARM_THREAD_STATE64_COUNT + ARM_EXCEPTION_STATE64_COUNT + ARM_NEON_STATE64_COUNT) * sizeof(int))
|
|
|
|
#if __arm64__
|
|
#define C_64_REDZONE_LEN 128
|
|
#endif
|
|
|
|
#define TRUNC_TO_16_BYTES(addr) (addr & ~0xf)
|
|
|
|
static int
|
|
sendsig_get_state32(thread_t th_act, arm_thread_state_t *ts, mcontext32_t *mcp)
|
|
{
|
|
void *tstate;
|
|
mach_msg_type_number_t state_count;
|
|
|
|
assert(!proc_is64bit_data(current_proc()));
|
|
|
|
tstate = (void *) ts;
|
|
state_count = ARM_THREAD_STATE_COUNT;
|
|
if (thread_getstatus(th_act, ARM_THREAD_STATE, (thread_state_t) tstate, &state_count) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
mcp->ss = *ts;
|
|
tstate = (void *) &mcp->ss;
|
|
state_count = ARM_THREAD_STATE_COUNT;
|
|
if (machine_thread_state_convert_to_user(th_act, ARM_THREAD_STATE, (thread_state_t) tstate,
|
|
&state_count, TSSF_FLAGS_NONE) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
tstate = (void *) &mcp->es;
|
|
state_count = ARM_EXCEPTION_STATE_COUNT;
|
|
if (thread_getstatus(th_act, ARM_EXCEPTION_STATE, (thread_state_t) tstate, &state_count) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
tstate = (void *) &mcp->fs;
|
|
state_count = ARM_VFP_STATE_COUNT;
|
|
if (thread_getstatus_to_user(th_act, ARM_VFP_STATE, (thread_state_t) tstate, &state_count, TSSF_FLAGS_NONE) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static TUNABLE(bool, pac_sigreturn_token, "pac_sigreturn_token", true);
|
|
|
|
#if defined(__arm64__)
|
|
struct user_sigframe64 {
|
|
/* We can pass the last two args in registers for ARM64 */
|
|
user64_siginfo_t sinfo;
|
|
struct user_ucontext64 uctx;
|
|
mcontext64_t mctx;
|
|
};
|
|
|
|
static int
|
|
sendsig_get_state64(thread_t th_act, arm_thread_state64_t *ts, mcontext64_t *mcp)
|
|
{
|
|
void *tstate;
|
|
mach_msg_type_number_t state_count;
|
|
|
|
assert(proc_is64bit_data(current_proc()));
|
|
|
|
tstate = (void *) ts;
|
|
state_count = ARM_THREAD_STATE64_COUNT;
|
|
if (thread_getstatus(th_act, ARM_THREAD_STATE64, (thread_state_t) tstate, &state_count) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
mcp->ss = *ts;
|
|
tstate = (void *) &mcp->ss;
|
|
state_count = ARM_THREAD_STATE64_COUNT;
|
|
thread_set_status_flags_t flags = TSSF_STASH_SIGRETURN_TOKEN;
|
|
if (pac_sigreturn_token || task_needs_user_signed_thread_state(current_task())) {
|
|
flags |= TSSF_THREAD_USER_DIV;
|
|
}
|
|
if (machine_thread_state_convert_to_user(th_act, ARM_THREAD_STATE64, (thread_state_t) tstate,
|
|
&state_count, flags) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
tstate = (void *) &mcp->es;
|
|
state_count = ARM_EXCEPTION_STATE64_COUNT;
|
|
if (thread_getstatus(th_act, ARM_EXCEPTION_STATE64, (thread_state_t) tstate, &state_count) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
tstate = (void *) &mcp->ns;
|
|
state_count = ARM_NEON_STATE64_COUNT;
|
|
if (thread_getstatus_to_user(th_act, ARM_NEON_STATE64, (thread_state_t) tstate, &state_count, TSSF_FLAGS_NONE) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sendsig_fill_uctx64(user_ucontext64_t *uctx, int oonstack, int mask, user64_addr_t sp, user64_size_t stack_size, user64_addr_t p_mctx)
|
|
{
|
|
bzero(uctx, sizeof(*uctx));
|
|
uctx->uc_onstack = oonstack;
|
|
uctx->uc_sigmask = mask;
|
|
uctx->uc_stack.ss_sp = sp;
|
|
uctx->uc_stack.ss_size = stack_size;
|
|
if (oonstack) {
|
|
uctx->uc_stack.ss_flags |= SS_ONSTACK;
|
|
}
|
|
uctx->uc_link = (user64_addr_t)0;
|
|
uctx->uc_mcsize = (user64_size_t) UC_FLAVOR_SIZE64;
|
|
uctx->uc_mcontext64 = (user64_addr_t) p_mctx;
|
|
}
|
|
|
|
static kern_return_t
|
|
sendsig_set_thread_state64(arm_thread_state64_t *regs,
|
|
user64_addr_t catcher, int infostyle, int sig, user64_addr_t p_sinfo,
|
|
user64_addr_t p_uctx, user64_addr_t token, user64_addr_t trampact, user64_addr_t sp, thread_t th_act)
|
|
{
|
|
assert(proc_is64bit_data(current_proc()));
|
|
|
|
regs->x[0] = catcher;
|
|
regs->x[1] = infostyle;
|
|
regs->x[2] = sig;
|
|
regs->x[3] = p_sinfo;
|
|
regs->x[4] = p_uctx;
|
|
regs->x[5] = token;
|
|
regs->pc = trampact;
|
|
regs->cpsr = PSR64_USER64_DEFAULT;
|
|
regs->sp = sp;
|
|
|
|
return thread_setstatus(th_act, ARM_THREAD_STATE64, (void *)regs, ARM_THREAD_STATE64_COUNT);
|
|
}
|
|
#endif /* defined(__arm64__) */
|
|
|
|
static void
|
|
sendsig_fill_uctx32(user_ucontext32_t *uctx, int oonstack, int mask, user_addr_t sp, user_size_t stack_size, user_addr_t p_mctx)
|
|
{
|
|
bzero(uctx, sizeof(*uctx));
|
|
uctx->uc_onstack = oonstack;
|
|
uctx->uc_sigmask = mask;
|
|
uctx->uc_stack.ss_sp = (user32_addr_t) sp;
|
|
uctx->uc_stack.ss_size = (user32_size_t) stack_size;
|
|
if (oonstack) {
|
|
uctx->uc_stack.ss_flags |= SS_ONSTACK;
|
|
}
|
|
uctx->uc_link = (user32_addr_t)0;
|
|
uctx->uc_mcsize = (user32_size_t) UC_FLAVOR_SIZE32;
|
|
uctx->uc_mcontext = (user32_addr_t) p_mctx;
|
|
}
|
|
|
|
static kern_return_t
|
|
sendsig_set_thread_state32(arm_thread_state_t *regs,
|
|
user32_addr_t catcher, int infostyle, int sig, user32_addr_t p_sinfo,
|
|
user32_addr_t trampact, user32_addr_t sp, thread_t th_act)
|
|
{
|
|
assert(!proc_is64bit_data(current_proc()));
|
|
|
|
regs->r[0] = catcher;
|
|
regs->r[1] = infostyle;
|
|
regs->r[2] = sig;
|
|
regs->r[3] = p_sinfo;
|
|
if (trampact & 1) {
|
|
regs->pc = trampact & ~1;
|
|
#if defined(__arm64__)
|
|
regs->cpsr = PSR64_USER32_DEFAULT | PSR64_MODE_USER32_THUMB;
|
|
#else
|
|
#error Unknown architecture.
|
|
#endif
|
|
} else {
|
|
regs->pc = trampact;
|
|
regs->cpsr = PSR_USERDFLT;
|
|
}
|
|
regs->sp = sp;
|
|
|
|
return thread_setstatus(th_act, ARM_THREAD_STATE, (void *)regs, ARM_THREAD_STATE_COUNT);
|
|
}
|
|
|
|
#if CONFIG_DTRACE
|
|
static void
|
|
sendsig_do_dtrace(uthread_t ut, user_siginfo_t *sinfo, int sig, user_addr_t catcher)
|
|
{
|
|
bzero((caddr_t)&(ut->t_dtrace_siginfo), sizeof(ut->t_dtrace_siginfo));
|
|
|
|
ut->t_dtrace_siginfo.si_signo = sinfo->si_signo;
|
|
ut->t_dtrace_siginfo.si_code = sinfo->si_code;
|
|
ut->t_dtrace_siginfo.si_pid = sinfo->si_pid;
|
|
ut->t_dtrace_siginfo.si_uid = sinfo->si_uid;
|
|
ut->t_dtrace_siginfo.si_status = sinfo->si_status;
|
|
/* XXX truncates faulting address to void * */
|
|
ut->t_dtrace_siginfo.si_addr = CAST_DOWN_EXPLICIT(void *, sinfo->si_addr);
|
|
|
|
/* Fire DTrace proc:::fault probe when signal is generated by hardware. */
|
|
switch (sig) {
|
|
case SIGILL: case SIGBUS: case SIGSEGV: case SIGFPE: case SIGTRAP:
|
|
DTRACE_PROC2(fault, int, (int)(ut->uu_code), siginfo_t *, &(ut->t_dtrace_siginfo));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* XXX truncates faulting address to uintptr_t */
|
|
DTRACE_PROC3(signal__handle, int, sig, siginfo_t *, &(ut->t_dtrace_siginfo),
|
|
void (*)(void), CAST_DOWN(uintptr_t, catcher));
|
|
}
|
|
#endif
|
|
|
|
struct user_sigframe32 {
|
|
user32_addr_t puctx;
|
|
user32_addr_t token;
|
|
user32_siginfo_t sinfo;
|
|
struct user_ucontext32 uctx;
|
|
mcontext32_t mctx;
|
|
};
|
|
|
|
/*
|
|
* Send an interrupt to process.
|
|
*
|
|
*/
|
|
void
|
|
sendsig(
|
|
struct proc * p,
|
|
user_addr_t catcher,
|
|
int sig,
|
|
int mask,
|
|
__unused uint32_t code,
|
|
sigset_t siginfo
|
|
)
|
|
{
|
|
union {
|
|
struct ts32 {
|
|
arm_thread_state_t ss;
|
|
} ts32;
|
|
#if defined(__arm64__)
|
|
struct ts64 {
|
|
arm_thread_state64_t ss;
|
|
} ts64;
|
|
#endif
|
|
} ts;
|
|
union {
|
|
struct user_sigframe32 uf32;
|
|
#if defined(__arm64__)
|
|
struct user_sigframe64 uf64;
|
|
#endif
|
|
} user_frame;
|
|
|
|
user_siginfo_t sinfo;
|
|
user_addr_t sp = 0, trampact;
|
|
struct sigacts *ps = &p->p_sigacts;
|
|
int oonstack, infostyle;
|
|
thread_t th_act;
|
|
struct uthread *ut;
|
|
user_size_t stack_size = 0;
|
|
user_addr_t p_uctx, token_uctx;
|
|
kern_return_t kr;
|
|
|
|
th_act = current_thread();
|
|
ut = get_bsdthread_info(th_act);
|
|
|
|
bzero(&ts, sizeof(ts));
|
|
bzero(&user_frame, sizeof(user_frame));
|
|
|
|
if (siginfo & sigmask(sig)) {
|
|
infostyle = UC_FLAVOR;
|
|
} else {
|
|
infostyle = UC_TRAD;
|
|
}
|
|
|
|
trampact = SIGTRAMP(p, sig);
|
|
oonstack = ut->uu_sigstk.ss_flags & SA_ONSTACK;
|
|
|
|
if (ut->uu_pending_sigreturn == 0) {
|
|
/* Generate random token value used to validate sigreturn arguments */
|
|
read_random(&ut->uu_sigreturn_token, sizeof(ut->uu_sigreturn_token));
|
|
|
|
do {
|
|
read_random(&ut->uu_sigreturn_diversifier, sizeof(ut->uu_sigreturn_diversifier));
|
|
ut->uu_sigreturn_diversifier &=
|
|
__DARWIN_ARM_THREAD_STATE64_USER_DIVERSIFIER_MASK;
|
|
} while (ut->uu_sigreturn_diversifier == 0);
|
|
}
|
|
ut->uu_pending_sigreturn++;
|
|
|
|
/*
|
|
* Get sundry thread state.
|
|
*/
|
|
if (proc_is64bit_data(p)) {
|
|
#ifdef __arm64__
|
|
int ret = 0;
|
|
if ((ret = sendsig_get_state64(th_act, &ts.ts64.ss, &user_frame.uf64.mctx)) != 0) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process [%s][%d] sendsig_get_state64 failed with ret %d, expected 0", p->p_comm, proc_getpid(p), ret);
|
|
#endif
|
|
goto bad2;
|
|
}
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
int ret = 0;
|
|
if ((ret = sendsig_get_state32(th_act, &ts.ts32.ss, &user_frame.uf32.mctx)) != 0) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process [%s][%d] sendsig_get_state32 failed with ret %d, expected 0", p->p_comm, proc_getpid(p), ret);
|
|
#endif
|
|
goto bad2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Figure out where our new stack lives.
|
|
*/
|
|
if ((ut->uu_flag & UT_ALTSTACK) && !oonstack &&
|
|
(ps->ps_sigonstack & sigmask(sig))) {
|
|
sp = ut->uu_sigstk.ss_sp;
|
|
stack_size = ut->uu_sigstk.ss_size;
|
|
|
|
sp += stack_size;
|
|
ut->uu_sigstk.ss_flags |= SA_ONSTACK;
|
|
} else {
|
|
/*
|
|
* Get stack pointer, and allocate enough space
|
|
* for signal handler data.
|
|
*/
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
sp = CAST_USER_ADDR_T(ts.ts64.ss.sp);
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
sp = CAST_USER_ADDR_T(ts.ts32.ss.sp);
|
|
}
|
|
}
|
|
|
|
/* Make sure to move stack pointer down for room for metadata */
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
sp = (sp - sizeof(user_frame.uf64) - C_64_REDZONE_LEN);
|
|
sp = TRUNC_TO_16_BYTES(sp);
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
sp -= sizeof(user_frame.uf32);
|
|
}
|
|
|
|
proc_unlock(p);
|
|
|
|
/*
|
|
* Fill in ucontext (points to mcontext, i.e. thread states).
|
|
*/
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
sendsig_fill_uctx64(&user_frame.uf64.uctx, oonstack, mask, sp, (user64_size_t)stack_size,
|
|
(user64_addr_t)&((struct user_sigframe64*)sp)->mctx);
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
sendsig_fill_uctx32(&user_frame.uf32.uctx, oonstack, mask, sp, (user32_size_t)stack_size,
|
|
(user32_addr_t)&((struct user_sigframe32*)sp)->mctx);
|
|
}
|
|
|
|
/*
|
|
* Setup siginfo.
|
|
*/
|
|
bzero((caddr_t) &sinfo, sizeof(sinfo));
|
|
sinfo.si_signo = sig;
|
|
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
sinfo.si_addr = ts.ts64.ss.pc;
|
|
sinfo.pad[0] = ts.ts64.ss.sp;
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
sinfo.si_addr = ts.ts32.ss.pc;
|
|
sinfo.pad[0] = ts.ts32.ss.sp;
|
|
}
|
|
|
|
switch (sig) {
|
|
case SIGILL:
|
|
#ifdef BER_XXX
|
|
if (mctx.ss.srr1 & (1 << (31 - SRR1_PRG_ILL_INS_BIT))) {
|
|
sinfo.si_code = ILL_ILLOPC;
|
|
} else if (mctx.ss.srr1 & (1 << (31 - SRR1_PRG_PRV_INS_BIT))) {
|
|
sinfo.si_code = ILL_PRVOPC;
|
|
} else if (mctx.ss.srr1 & (1 << (31 - SRR1_PRG_TRAP_BIT))) {
|
|
sinfo.si_code = ILL_ILLTRP;
|
|
} else {
|
|
sinfo.si_code = ILL_NOOP;
|
|
}
|
|
#else
|
|
sinfo.si_code = ILL_ILLTRP;
|
|
#endif
|
|
break;
|
|
|
|
case SIGFPE:
|
|
switch (ut->uu_code) {
|
|
case EXC_ARM_FP_UF:
|
|
sinfo.si_code = FPE_FLTUND;
|
|
break;
|
|
case EXC_ARM_FP_OF:
|
|
sinfo.si_code = FPE_FLTOVF;
|
|
break;
|
|
case EXC_ARM_FP_IO:
|
|
sinfo.si_code = FPE_FLTINV;
|
|
break;
|
|
case EXC_ARM_FP_DZ:
|
|
sinfo.si_code = FPE_FLTDIV;
|
|
break;
|
|
case EXC_ARM_FP_ID:
|
|
sinfo.si_code = FPE_FLTINV;
|
|
break;
|
|
case EXC_ARM_FP_IX:
|
|
sinfo.si_code = FPE_FLTRES;
|
|
break;
|
|
default:
|
|
sinfo.si_code = FPE_NOOP;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case SIGBUS:
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
sinfo.si_addr = user_frame.uf64.mctx.es.far;
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
sinfo.si_addr = user_frame.uf32.mctx.es.far;
|
|
}
|
|
|
|
sinfo.si_code = BUS_ADRALN;
|
|
break;
|
|
|
|
case SIGSEGV:
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
sinfo.si_addr = user_frame.uf64.mctx.es.far;
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
sinfo.si_addr = user_frame.uf32.mctx.es.far;
|
|
}
|
|
|
|
#ifdef BER_XXX
|
|
/* First check in srr1 and then in dsisr */
|
|
if (mctx.ss.srr1 & (1 << (31 - DSISR_PROT_BIT))) {
|
|
sinfo.si_code = SEGV_ACCERR;
|
|
} else if (mctx.es.dsisr & (1 << (31 - DSISR_PROT_BIT))) {
|
|
sinfo.si_code = SEGV_ACCERR;
|
|
} else {
|
|
sinfo.si_code = SEGV_MAPERR;
|
|
}
|
|
#else
|
|
sinfo.si_code = SEGV_ACCERR;
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
{
|
|
int status_and_exitcode;
|
|
|
|
/*
|
|
* All other signals need to fill out a minimum set of
|
|
* information for the siginfo structure passed into
|
|
* the signal handler, if SA_SIGINFO was specified.
|
|
*
|
|
* p->si_status actually contains both the status and
|
|
* the exit code; we save it off in its own variable
|
|
* for later breakdown.
|
|
*/
|
|
proc_lock(p);
|
|
sinfo.si_pid = p->si_pid;
|
|
p->si_pid = 0;
|
|
status_and_exitcode = p->si_status;
|
|
p->si_status = 0;
|
|
sinfo.si_uid = p->si_uid;
|
|
p->si_uid = 0;
|
|
sinfo.si_code = p->si_code;
|
|
p->si_code = 0;
|
|
proc_unlock(p);
|
|
if (sinfo.si_code == CLD_EXITED) {
|
|
if (WIFEXITED(status_and_exitcode)) {
|
|
sinfo.si_code = CLD_EXITED;
|
|
} else if (WIFSIGNALED(status_and_exitcode)) {
|
|
if (WCOREDUMP(status_and_exitcode)) {
|
|
sinfo.si_code = CLD_DUMPED;
|
|
status_and_exitcode = W_EXITCODE(status_and_exitcode, status_and_exitcode);
|
|
} else {
|
|
sinfo.si_code = CLD_KILLED;
|
|
status_and_exitcode = W_EXITCODE(status_and_exitcode, status_and_exitcode);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* The recorded status contains the exit code and the
|
|
* signal information, but the information to be passed
|
|
* in the siginfo to the handler is supposed to only
|
|
* contain the status, so we have to shift it out.
|
|
*/
|
|
sinfo.si_status = (WEXITSTATUS(status_and_exitcode) & 0x00FFFFFF) | (((uint32_t)(p->p_xhighbits) << 24) & 0xFF000000);
|
|
p->p_xhighbits = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if CONFIG_DTRACE
|
|
sendsig_do_dtrace(ut, &sinfo, sig, catcher);
|
|
#endif /* CONFIG_DTRACE */
|
|
|
|
/*
|
|
* Copy signal-handling frame out to user space, set thread state.
|
|
*/
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
user64_addr_t token;
|
|
|
|
/*
|
|
* mctx filled in when we get state. uctx filled in by
|
|
* sendsig_fill_uctx64(). We fill in the sinfo now.
|
|
*/
|
|
siginfo_user_to_user64(&sinfo, &user_frame.uf64.sinfo);
|
|
|
|
p_uctx = (user_addr_t)&((struct user_sigframe64*)sp)->uctx;
|
|
/*
|
|
* Generate the validation token for sigreturn
|
|
*/
|
|
token_uctx = p_uctx;
|
|
kr = machine_thread_siguctx_pointer_convert_to_user(th_act, &token_uctx);
|
|
assert(kr == KERN_SUCCESS);
|
|
token = (user64_addr_t)token_uctx ^ (user64_addr_t)ut->uu_sigreturn_token;
|
|
|
|
int ret = 0;
|
|
if ((ret = copyout(&user_frame.uf64, sp, sizeof(user_frame.uf64))) != 0) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process [%s][%d] copyout of user_frame to (sp, size) = (0x%llx, %zu) failed with ret %d, expected 0\n", p->p_comm, proc_getpid(p), sp, sizeof(user_frame.uf64), ret);
|
|
#endif
|
|
goto bad;
|
|
}
|
|
|
|
if ((kr = sendsig_set_thread_state64(&ts.ts64.ss,
|
|
catcher, infostyle, sig, (user64_addr_t)&((struct user_sigframe64*)sp)->sinfo,
|
|
(user64_addr_t)p_uctx, token, trampact, sp, th_act)) != KERN_SUCCESS) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process [%s][%d] sendsig_set_thread_state64 failed with kr %d, expected 0", p->p_comm, proc_getpid(p), kr);
|
|
#endif
|
|
goto bad;
|
|
}
|
|
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
user32_addr_t token;
|
|
|
|
/*
|
|
* mctx filled in when we get state. uctx filled in by
|
|
* sendsig_fill_uctx32(). We fill in the sinfo, *pointer*
|
|
* to uctx and token now.
|
|
*/
|
|
siginfo_user_to_user32(&sinfo, &user_frame.uf32.sinfo);
|
|
|
|
p_uctx = (user_addr_t)&((struct user_sigframe32*)sp)->uctx;
|
|
/*
|
|
* Generate the validation token for sigreturn
|
|
*/
|
|
token_uctx = (user_addr_t)p_uctx;
|
|
kr = machine_thread_siguctx_pointer_convert_to_user(th_act, &token_uctx);
|
|
assert(kr == KERN_SUCCESS);
|
|
token = (user32_addr_t)token_uctx ^ (user32_addr_t)ut->uu_sigreturn_token;
|
|
|
|
user_frame.uf32.puctx = (user32_addr_t)p_uctx;
|
|
user_frame.uf32.token = token;
|
|
|
|
if (copyout(&user_frame.uf32, sp, sizeof(user_frame.uf32)) != 0) {
|
|
goto bad;
|
|
}
|
|
|
|
if (sendsig_set_thread_state32(&ts.ts32.ss,
|
|
CAST_DOWN_EXPLICIT(user32_addr_t, catcher), infostyle, sig, (user32_addr_t)&((struct user_sigframe32*)sp)->sinfo,
|
|
CAST_DOWN_EXPLICIT(user32_addr_t, trampact), CAST_DOWN_EXPLICIT(user32_addr_t, sp), th_act) != KERN_SUCCESS) {
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
proc_lock(p);
|
|
return;
|
|
|
|
bad:
|
|
proc_lock(p);
|
|
bad2:
|
|
assert(ut->uu_pending_sigreturn > 0);
|
|
ut->uu_pending_sigreturn--;
|
|
proc_set_sigact(p, SIGILL, SIG_DFL);
|
|
sig = sigmask(SIGILL);
|
|
p->p_sigignore &= ~sig;
|
|
p->p_sigcatch &= ~sig;
|
|
ut->uu_sigmask &= ~sig;
|
|
/* sendsig is called with signal lock held */
|
|
proc_unlock(p);
|
|
psignal_locked(p, SIGILL);
|
|
proc_lock(p);
|
|
}
|
|
|
|
/*
|
|
* System call to cleanup state after a signal
|
|
* has been taken. Reset signal mask and
|
|
* stack state from context left by sendsig (above).
|
|
* Return to previous * context left by sendsig.
|
|
* Check carefully to * make sure that the user has not
|
|
* modified the * spr to gain improper priviledges.
|
|
*/
|
|
|
|
static int
|
|
sigreturn_copyin_ctx32(struct user_ucontext32 *uctx, mcontext32_t *mctx, user_addr_t uctx_addr)
|
|
{
|
|
int error;
|
|
|
|
assert(!proc_is64bit_data(current_proc()));
|
|
|
|
error = copyin(uctx_addr, uctx, sizeof(*uctx));
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
/* validate the machine context size */
|
|
switch (uctx->uc_mcsize) {
|
|
case UC_FLAVOR_SIZE32:
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
assert(uctx->uc_mcsize == sizeof(*mctx));
|
|
error = copyin((user_addr_t)uctx->uc_mcontext, mctx, uctx->uc_mcsize);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sigreturn_set_state32(thread_t th_act, mcontext32_t *mctx)
|
|
{
|
|
assert(!proc_is64bit_data(current_proc()));
|
|
|
|
/* validate the thread state, set/reset appropriate mode bits in cpsr */
|
|
#if defined(__arm64__)
|
|
mctx->ss.cpsr = (mctx->ss.cpsr & ~PSR64_MODE_MASK) | PSR64_USER32_DEFAULT;
|
|
#else
|
|
#error Unknown architecture.
|
|
#endif
|
|
|
|
if (thread_setstatus_from_user(th_act, ARM_THREAD_STATE, (void *)&mctx->ss,
|
|
ARM_THREAD_STATE_COUNT, NULL, 0, TSSF_FLAGS_NONE) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
if (thread_setstatus_from_user(th_act, ARM_VFP_STATE, (void *)&mctx->fs,
|
|
ARM_VFP_STATE_COUNT, NULL, 0, TSSF_FLAGS_NONE) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(__arm64__)
|
|
static int
|
|
sigreturn_copyin_ctx64(struct user_ucontext64 *uctx, mcontext64_t *mctx, user_addr_t uctx_addr)
|
|
{
|
|
int error;
|
|
|
|
assert(proc_is64bit_data(current_proc()));
|
|
|
|
error = copyin(uctx_addr, uctx, sizeof(*uctx));
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
/* validate the machine context size */
|
|
switch (uctx->uc_mcsize) {
|
|
case UC_FLAVOR_SIZE64:
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
assert(uctx->uc_mcsize == sizeof(*mctx));
|
|
error = copyin((user_addr_t)uctx->uc_mcontext64, mctx, uctx->uc_mcsize);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sigreturn_set_state64(thread_t th_act, mcontext64_t *mctx, thread_set_status_flags_t tssf_flags)
|
|
{
|
|
assert(proc_is64bit_data(current_proc()));
|
|
|
|
/* validate the thread state, set/reset appropriate mode bits in cpsr */
|
|
mctx->ss.cpsr = (mctx->ss.cpsr & ~PSR64_MODE_MASK) | PSR64_USER64_DEFAULT;
|
|
|
|
if (thread_setstatus_from_user(th_act, ARM_THREAD_STATE64, (void *)&mctx->ss,
|
|
ARM_THREAD_STATE64_COUNT, NULL, 0, tssf_flags) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
if (thread_setstatus_from_user(th_act, ARM_NEON_STATE64, (void *)&mctx->ns,
|
|
ARM_NEON_STATE64_COUNT, NULL, 0, TSSF_FLAGS_NONE) != KERN_SUCCESS) {
|
|
return EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(__arm64__) */
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
sigreturn(
|
|
struct proc * p,
|
|
struct sigreturn_args * uap,
|
|
__unused int *retval)
|
|
{
|
|
union {
|
|
user_ucontext32_t uc32;
|
|
#if defined(__arm64__)
|
|
user_ucontext64_t uc64;
|
|
#endif
|
|
} uctx;
|
|
|
|
union {
|
|
mcontext32_t mc32;
|
|
#if defined(__arm64__)
|
|
mcontext64_t mc64;
|
|
#endif
|
|
} mctx;
|
|
|
|
struct sigacts *ps = &p->p_sigacts;
|
|
int error, sigmask = 0, onstack = 0;
|
|
thread_t th_act;
|
|
struct uthread *ut;
|
|
uint32_t sigreturn_validation;
|
|
user_addr_t token_uctx;
|
|
kern_return_t kr;
|
|
|
|
th_act = current_thread();
|
|
ut = (struct uthread *) get_bsdthread_info(th_act);
|
|
|
|
/* see osfmk/kern/restartable.c */
|
|
act_set_ast_reset_pcs(TASK_NULL, th_act);
|
|
|
|
/*
|
|
* If we are being asked to change the altstack flag on the thread, we
|
|
* just set/reset it and return (the uap->uctx is not used).
|
|
*/
|
|
if ((unsigned int)uap->infostyle == UC_SET_ALT_STACK) {
|
|
ut->uu_sigstk.ss_flags |= SA_ONSTACK;
|
|
return 0;
|
|
} else if ((unsigned int)uap->infostyle == UC_RESET_ALT_STACK) {
|
|
ut->uu_sigstk.ss_flags &= ~SA_ONSTACK;
|
|
return 0;
|
|
}
|
|
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
error = sigreturn_copyin_ctx64(&uctx.uc64, &mctx.mc64, uap->uctx);
|
|
if (error != 0) {
|
|
return error;
|
|
}
|
|
|
|
onstack = uctx.uc64.uc_onstack;
|
|
sigmask = uctx.uc64.uc_sigmask;
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
error = sigreturn_copyin_ctx32(&uctx.uc32, &mctx.mc32, uap->uctx);
|
|
if (error != 0) {
|
|
return error;
|
|
}
|
|
|
|
onstack = uctx.uc32.uc_onstack;
|
|
sigmask = uctx.uc32.uc_sigmask;
|
|
}
|
|
|
|
if ((onstack & 01)) {
|
|
ut->uu_sigstk.ss_flags |= SA_ONSTACK;
|
|
} else {
|
|
ut->uu_sigstk.ss_flags &= ~SA_ONSTACK;
|
|
}
|
|
|
|
ut->uu_sigmask = sigmask & ~sigcantmask;
|
|
if (ut->uu_siglist & ~ut->uu_sigmask) {
|
|
signal_setast(current_thread());
|
|
}
|
|
|
|
sigreturn_validation = atomic_load_explicit(
|
|
&ps->ps_sigreturn_validation, memory_order_relaxed);
|
|
token_uctx = uap->uctx;
|
|
kr = machine_thread_siguctx_pointer_convert_to_user(th_act, &token_uctx);
|
|
assert(kr == KERN_SUCCESS);
|
|
|
|
if (proc_is64bit_data(p)) {
|
|
#if defined(__arm64__)
|
|
user64_addr_t token;
|
|
token = (user64_addr_t)token_uctx ^ (user64_addr_t)ut->uu_sigreturn_token;
|
|
thread_set_status_flags_t tssf_flags = TSSF_FLAGS_NONE;
|
|
|
|
if ((user64_addr_t)uap->token != token) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process %s[%d] sigreturn token mismatch: received 0x%llx expected 0x%llx\n",
|
|
p->p_comm, proc_getpid(p), (user64_addr_t)uap->token, token);
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
if (sigreturn_validation != PS_SIGRETURN_VALIDATION_DISABLED) {
|
|
return EINVAL;
|
|
}
|
|
}
|
|
|
|
if (sigreturn_validation != PS_SIGRETURN_VALIDATION_DISABLED) {
|
|
tssf_flags |= TSSF_CHECK_SIGRETURN_TOKEN;
|
|
|
|
if (pac_sigreturn_token || task_needs_user_signed_thread_state(current_task())) {
|
|
tssf_flags |= TSSF_ALLOW_ONLY_MATCHING_TOKEN | TSSF_THREAD_USER_DIV;
|
|
}
|
|
}
|
|
error = sigreturn_set_state64(th_act, &mctx.mc64, tssf_flags);
|
|
if (error != 0) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process %s[%d] sigreturn set_state64 error %d\n",
|
|
p->p_comm, proc_getpid(p), error);
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
return error;
|
|
}
|
|
#else
|
|
#error Unsupported architecture
|
|
#endif
|
|
} else {
|
|
user32_addr_t token;
|
|
token = (user32_addr_t)token_uctx ^ (user32_addr_t)ut->uu_sigreturn_token;
|
|
if ((user32_addr_t)uap->token != token) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process %s[%d] sigreturn token mismatch: received 0x%x expected 0x%x\n",
|
|
p->p_comm, proc_getpid(p), (user32_addr_t)uap->token, token);
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
if (sigreturn_validation != PS_SIGRETURN_VALIDATION_DISABLED) {
|
|
return EINVAL;
|
|
}
|
|
}
|
|
error = sigreturn_set_state32(th_act, &mctx.mc32);
|
|
if (error != 0) {
|
|
#if DEVELOPMENT || DEBUG
|
|
printf("process %s[%d] sigreturn sigreturn_set_state32 error %d\n",
|
|
p->p_comm, proc_getpid(p), error);
|
|
#endif /* DEVELOPMENT || DEBUG */
|
|
return error;
|
|
}
|
|
}
|
|
|
|
/* Decrement the pending sigreturn count */
|
|
if (ut->uu_pending_sigreturn > 0) {
|
|
ut->uu_pending_sigreturn--;
|
|
}
|
|
|
|
return EJUSTRETURN;
|
|
}
|
|
|
|
/*
|
|
* machine_exception() performs machine-dependent translation
|
|
* of a mach exception to a unix signal.
|
|
*/
|
|
int
|
|
machine_exception(int exception,
|
|
__unused mach_exception_code_t code,
|
|
__unused mach_exception_subcode_t subcode)
|
|
{
|
|
switch (exception) {
|
|
case EXC_BAD_INSTRUCTION:
|
|
return SIGILL;
|
|
|
|
case EXC_ARITHMETIC:
|
|
return SIGFPE;
|
|
}
|
|
|
|
return 0;
|
|
}
|