diff --git a/LibOS/shim/include/shim_types.h b/LibOS/shim/include/shim_types.h index e3e1af7e..f68a8cb6 100644 --- a/LibOS/shim/include/shim_types.h +++ b/LibOS/shim/include/shim_types.h @@ -372,15 +372,24 @@ typedef struct { /* Userlevel context. */ typedef struct ucontext { -#define UC_FP_XSTATE 0x1 -#define UC_SIGCONTEXT_SS 0x2 -#define UC_STRICT_RESTORE_SS 0x4 +#ifndef UC_FP_XSTATE +#define UC_FP_XSTATE 0x1 +#endif +#ifndef UC_SIGCONTEXT_SS +#define UC_SIGCONTEXT_SS 0x2 +#endif +#ifndef UC_STRICT_RESTORE_SS +#define UC_STRICT_RESTORE_SS 0x4 +#endif unsigned long int uc_flags; struct ucontext *uc_link; -// stack_t::ss_flags -#define SS_ONSTACK 1 -#define SS_DISABLE 2 -#define SS_AUTODISARM (1U << 31) /* disable sas during sighandling */ +/* stack_t::ss_flags */ +#ifndef SS_ONSTACK +#define SS_ONSTACK 1 +#endif +#ifndef SS_DISABLE +#define SS_DISABLE 2 +#endif stack_t uc_stack; mcontext_t uc_mcontext; __sigset_t uc_sigmask; diff --git a/LibOS/shim/src/bookkeep/shim_signal.c b/LibOS/shim/src/bookkeep/shim_signal.c index 044dea2f..84e4a265 100644 --- a/LibOS/shim/src/bookkeep/shim_signal.c +++ b/LibOS/shim/src/bookkeep/shim_signal.c @@ -125,7 +125,7 @@ void deliver_signal (siginfo_t * info, PAL_CONTEXT * context) struct shim_thread * cur_thread = (struct shim_thread *) tcb->tp; int sig = info->si_signo; - struct shim_signal* signal = malloc(sizeof(*signal)); + struct shim_signal* signal = calloc(1, sizeof(*signal)); if (!signal) { return; } @@ -133,7 +133,6 @@ void deliver_signal (siginfo_t * info, PAL_CONTEXT * context) int64_t preempt = __disable_preempt(tcb); /* save in signal */ - memset(signal, 0, sizeof(struct shim_signal)); __store_info(info, signal); struct shim_signal** signal_log = allocate_signal_log(cur_thread, sig); if (signal_log) { @@ -526,7 +525,7 @@ static void syscallas_return_emulate(PAL_CONTEXT* context) { * After this emulation, the context is updated to app context, * and the caller can proceed to handle the async signal in a safe manner. */ - void* rip = (void *)context->IP; + void* rip = (void*)context->IP; if (rip == (void*)&__syscallas_return_before_jmp) { /* emulate jmp *%gs:(SHIM_TCB_OFFSET + SHIM_TCB_TMP_RIP) */ shim_tcb_t* tcb = shim_get_tcb(); @@ -651,12 +650,17 @@ __sigset_t * set_sig_mask (struct shim_thread * thread, static void __get_sighandler(struct shim_thread* thread, int sig, __rt_sighandler_t* handler, restorer_t* restorer) { + assert(locked(&thread->lock)); *handler = NULL; *restorer = NULL; struct shim_signal_handle* sighdl = &thread->signal_handles[sig - 1]; if (sighdl->action) { struct __kernel_sigaction* act = sighdl->action; + /* + * on amd64, 1-3 arguments are passed by register and when setting up the signal frame, + * all the 3 arguments are setup, so this cast is safe. + */ *handler = (void*)act->k_sa_handler; *restorer = act->sa_restorer; if (act->sa_flags & SA_RESETHAND) { @@ -667,7 +671,7 @@ static void __get_sighandler(struct shim_thread* thread, int sig, if ((void*)*handler == SIG_IGN) { *handler = NULL; - } else if (!*handler) { + } else if ((void*)*handler == SIG_DFL || !*handler) { *handler = default_sighandler[sig - 1]; } } @@ -679,15 +683,15 @@ static void get_sighandler(struct shim_thread * thread, int sig, unlock(&thread->lock); } -static unsigned int fpstate_size_get(const struct _libc_fpstate* fpstate) { - if (fpstate == NULL) +static unsigned int xstate_size_get(const struct _libc_xregs_state* xstate) { + if (xstate == NULL) return 0; - const struct _fpx_sw_bytes* sw = &fpstate->sw_reserved; - if (sw->magic1 == FP_XSTATE_MAGIC1 && + const struct _libc_fpx_sw_bytes* sw = &xstate->fpstate.sw_reserved; + if (sw->magic1 == _LIBC_FP_XSTATE_MAGIC1 && sw->xstate_size < sw->extended_size && - *((__typeof__(FP_XSTATE_MAGIC2)*)((void*)fpstate + sw->xstate_size)) == - FP_XSTATE_MAGIC2) + *((__typeof__(_LIBC_FP_XSTATE_MAGIC2)*)((char*)xstate + sw->xstate_size)) == + _LIBC_FP_XSTATE_MAGIC2) return sw->extended_size; return sizeof(struct swregs_state); @@ -696,8 +700,8 @@ static unsigned int fpstate_size_get(const struct _libc_fpstate* fpstate) { static void direct_call_if_default_handler(int sig, siginfo_t* info, __rt_sighandler_t handler); static void get_signal_stack(struct shim_thread* thread, void* current_stack, - unsigned int fpstate_size, - struct sigframe** user_sigframe, fpregset_t* user_fpstate) { + unsigned int xstate_size, struct sigframe** user_sigframe, + struct _libc_xregs_state** user_xstate) { void* sp; const stack_t* ss = &thread->signal_altstack; @@ -709,10 +713,20 @@ static void get_signal_stack(struct shim_thread* thread, void* current_stack, sp = ss->ss_sp + ss->ss_size; } - sp = ALIGN_DOWN_PTR(sp - fpstate_size, _LIBC_XSTATE_ALIGN); - *user_fpstate = sp; - sp = ALIGN_DOWN_PTR(sp - sizeof(**user_sigframe), 16UL) - 8; - *user_sigframe = sp; + sp = ALIGN_DOWN_PTR(sp - xstate_size, _LIBC_XSTATE_ALIGN); + *user_xstate = sp; + + /* + * signal frame requires those alignment on stack + * struct sigframe + * void* restorer; (8 mod 16) bytes aligned + * as if right after function call + * ucontext_t uc; 16 bytes aligned as if before function call + * ... + */ + sp = ALIGN_DOWN_PTR(sp - (sizeof(**user_sigframe) - offsetof(struct sigframe, uc)), 16UL); + ucontext_t* user_uc = sp; + *user_sigframe = container_of(user_uc, struct sigframe, uc); assert(IS_ALIGNED_PTR(&(*user_sigframe)->uc, 16UL)); } @@ -720,15 +734,12 @@ static void setup_sigframe(struct shim_thread* thread, int sig, struct shim_sign PAL_CONTEXT* context, __rt_sighandler_t handler, restorer_t restorer) { direct_call_if_default_handler(sig, &signal->info, handler); - struct _libc_xregs_state* xregs_state = - (struct _libc_xregs_state* )context->fpregs; - struct _libc_fpstate* fpstate = &xregs_state->fpstate; - unsigned int fpstate_size = fpstate_size_get(fpstate); + struct _libc_xregs_state* xstate = (struct _libc_xregs_state*)context->fpregs; + unsigned int xstate_size = xstate_size_get(xstate); struct sigframe* user_sigframe; - fpregset_t user_fpstate; - get_signal_stack(thread, (void*)context->rsp, fpstate_size, - &user_sigframe, &user_fpstate); + struct _libc_xregs_state* user_xstate; + get_signal_stack(thread, (void*)context->rsp, xstate_size, &user_sigframe, &user_xstate); user_sigframe->restorer = restorer; ucontext_t* user_uc = &user_sigframe->uc; @@ -741,9 +752,9 @@ static void setup_sigframe(struct shim_thread* thread, int sig, struct shim_sign sizeof(user_uc->uc_mcontext.gregs)); user_sigframe->info = signal->info; - if (fpstate_size > 0) { - user_uc->uc_mcontext.fpregs = user_fpstate; - memcpy(user_fpstate, fpstate, fpstate_size); + if (xstate_size > 0) { + user_uc->uc_mcontext.fpregs = &user_xstate->fpstate; + memcpy(user_xstate, xstate, xstate_size); if (fpu_xstate_enabled) { user_uc->uc_flags |= UC_FP_XSTATE; } @@ -845,6 +856,15 @@ void handle_sysret_signal(void) { struct shim_thread* thread = (struct shim_thread*)tcb->tp; clear_bit(SHIM_FLAG_MAY_DELIVER_SIGNAL, &tcb->flags); + /* + * The host signal handler, allocate_signal_log(), can queue the signal to set the bit + * asynchronously. + * the checking order is important. + * clear the bit, test the condition and set the bit if signal delivery to app is necessary. + * + * False positive is acceptable as deliver_signal_on_sysret() is nop unless deliverable + * signal is queued. (except performance). + */ /* TODO: take user signal mask into account; would require peek_signal_log() */ if (atomic_read(&thread->has_signal)) { set_bit(SHIM_FLAG_MAY_DELIVER_SIGNAL, &tcb->flags); @@ -928,7 +948,7 @@ int handle_next_signal(ucontext_t* user_uc) { return 0; struct shim_regs* regs = shim_get_tcb()->context.regs; - struct sigframe* user_sigframe = (struct sigframe*)(((void*)user_uc) - 8); + struct sigframe* user_sigframe = container_of(user_uc, struct sigframe, uc); /* setup to return to signal handler */ user_sigframe->restorer = deliver.restorer; @@ -977,8 +997,8 @@ attribute_nofp uint64_t deliver_signal_on_sysret(uint64_t syscall_ret) { struct shim_thread* thread = tcb->tp; struct sigframe* user_sigframe; - fpregset_t user_fpstate; - get_signal_stack(thread, (void*)regs->rsp, fpu_xstate_size, &user_sigframe, &user_fpstate); + struct _libc_xregs_state* user_xstate; + get_signal_stack(thread, (void*)regs->rsp, fpu_xstate_size, &user_sigframe, &user_xstate); /* setup sigframe */ user_sigframe->restorer = restorer; @@ -987,42 +1007,42 @@ attribute_nofp uint64_t deliver_signal_on_sysret(uint64_t syscall_ret) { user_uc->uc_link = NULL; user_uc->uc_stack = thread->signal_altstack; - gregset_t * gregs = &user_uc->uc_mcontext.gregs; - (*gregs)[REG_R8] = regs->r8; - (*gregs)[REG_R9] = regs->r9; - (*gregs)[REG_R10] = regs->r10; - (*gregs)[REG_R11] = regs->r11; - (*gregs)[REG_R12] = regs->r12; - (*gregs)[REG_R13] = regs->r13; - (*gregs)[REG_R14] = regs->r14; - (*gregs)[REG_R15] = regs->r15; - (*gregs)[REG_RDI] = regs->rdi; - (*gregs)[REG_RSI] = regs->rsi; - (*gregs)[REG_RBP] = regs->rbp; - (*gregs)[REG_RBX] = regs->rbx; - (*gregs)[REG_RDX] = regs->rdx; - (*gregs)[REG_RAX] = syscall_ret; - (*gregs)[REG_RCX] = regs->rcx; - (*gregs)[REG_RSP] = regs->rsp; - (*gregs)[REG_RIP] = regs->rip; - (*gregs)[REG_EFL] = regs->rflags; + greg_t* gregs = user_uc->uc_mcontext.gregs; + gregs[REG_R8] = regs->r8; + gregs[REG_R9] = regs->r9; + gregs[REG_R10] = regs->r10; + gregs[REG_R11] = regs->r11; + gregs[REG_R12] = regs->r12; + gregs[REG_R13] = regs->r13; + gregs[REG_R14] = regs->r14; + gregs[REG_R15] = regs->r15; + gregs[REG_RDI] = regs->rdi; + gregs[REG_RSI] = regs->rsi; + gregs[REG_RBP] = regs->rbp; + gregs[REG_RBX] = regs->rbx; + gregs[REG_RDX] = regs->rdx; + gregs[REG_RAX] = syscall_ret; + gregs[REG_RCX] = regs->rcx; + gregs[REG_RSP] = regs->rsp; + gregs[REG_RIP] = regs->rip; + gregs[REG_EFL] = regs->rflags; union csgsfs sr = { - .cs = 0x33, /* __USER_CS(5) | 0(GDT) | 3(RPL) */ + .cs = 0x33, /* __USER_CS = (6 << 5) | (0 << 2)(GDT) | 3(RPL) */ .fs = 0, .gs = 0, - .ss = 0x2b, /* __USER_DS(6) | 0(GDT) | 3(RPL) */ + .ss = 0x2b, /* __USER_DS = (5 << 5) | (0 << 2)(GDT) | 3(RPL) */ }; - (*gregs)[REG_CSGSFS] = sr.csgsfs; + gregs[REG_CSGSFS] = sr.csgsfs; - (*gregs)[REG_ERR] = signal->info.si_errno; - (*gregs)[REG_TRAPNO] = signal->info.si_code; - (*gregs)[REG_OLDMASK] = 0; - (*gregs)[REG_CR2] = (long)signal->info.si_addr; + gregs[REG_ERR] = signal->info.si_errno; + gregs[REG_TRAPNO] = signal->info.si_code; + gregs[REG_OLDMASK] = 0; + gregs[REG_CR2] = (long)signal->info.si_addr; user_sigframe->info = signal->info; - user_uc->uc_mcontext.fpregs = user_fpstate; - memset(user_fpstate, 0, fpu_xstate_size); - fpstate_save(user_fpstate); + user_uc->uc_mcontext.fpregs = &user_xstate->fpstate; + memset(user_xstate, 0, fpu_xstate_size); + xstate_save(user_xstate); if (fpu_xstate_enabled) { user_uc->uc_flags |= UC_FP_XSTATE; } @@ -1033,7 +1053,7 @@ attribute_nofp uint64_t deliver_signal_on_sysret(uint64_t syscall_ret) { free(signal); /* setup to return to signal handler */ - fpstate_reset(); + xstate_reset(); regs->rsp = (uint64_t)user_sigframe; regs->rip = (unsigned long)handler; regs->rdi = (unsigned long)sig; @@ -1044,6 +1064,8 @@ attribute_nofp uint64_t deliver_signal_on_sysret(uint64_t syscall_ret) { // Need to hold thread->lock when calling this function void append_signal(struct shim_thread* thread, int sig, siginfo_t* info, bool need_interrupt) { + assert(locked(&thread->lock)); + __rt_sighandler_t handler; restorer_t restorer; __get_sighandler(thread, sig, &handler, &restorer); @@ -1140,13 +1162,13 @@ static void sighandler_core (int sig, siginfo_t * info, void * ucontext) sighandler_kill(sig, info, ucontext); } -static void direct_call_if_default_handler( - int sig, siginfo_t* info, __rt_sighandler_t handler) { +static void direct_call_if_default_handler(int sig, siginfo_t* info, __rt_sighandler_t handler) { /* sighandler_kill/core kills the thread without using info or context, so invoke it directly */ if (handler == &sighandler_kill || handler == &sighandler_core) { debug("directly calling sighandler_kill\n"); /* thread exits immediately after handling */ handler(sig, info, NULL); + __builtin_unreachable(); } } diff --git a/LibOS/shim/src/shim_init.c b/LibOS/shim/src/shim_init.c index 7d962262..0a2b0bbe 100644 --- a/LibOS/shim/src/shim_init.c +++ b/LibOS/shim/src/shim_init.c @@ -120,15 +120,15 @@ attribute_nofp void fpstate_save(struct _libc_fpstate* fpstate) { : "memory"); } - struct _fpx_sw_bytes* fpx_sw = &fpstate->sw_reserved; - fpx_sw->magic1 = FP_XSTATE_MAGIC1; - fpx_sw->extended_size = fpu_xstate_size + FP_XSTATE_MAGIC2_SIZE; + struct _libc_fpx_sw_bytes* fpx_sw = &fpstate->sw_reserved; + fpx_sw->magic1 = _LIBC_FP_XSTATE_MAGIC1; + fpx_sw->extended_size = fpu_xstate_size + _LIBC_FP_XSTATE_MAGIC2_SIZE; fpx_sw->xfeatures = fpu_xfeatures; fpx_sw->xstate_size = fpu_xstate_size; memset(fpx_sw->padding, 0, sizeof(fpx_sw->padding)); if (fpu_xstate_enabled) { - *((__typeof__(FP_XSTATE_MAGIC2)*)((void*)fpstate + fpx_sw->xstate_size)) - = FP_XSTATE_MAGIC2; + *((__typeof__(_LIBC_FP_XSTATE_MAGIC2)*)((char*)fpstate + fpx_sw->xstate_size)) + = _LIBC_FP_XSTATE_MAGIC2; } } diff --git a/LibOS/shim/src/sys/shim_sigaction.c b/LibOS/shim/src/sys/shim_sigaction.c index 1b69fab4..c5189208 100644 --- a/LibOS/shim/src/sys/shim_sigaction.c +++ b/LibOS/shim/src/sys/shim_sigaction.c @@ -91,43 +91,38 @@ attribute_nofp int shim_do_sigreturn(int __unused) { struct shim_regs* regs = tcb->context.regs; ucontext_t* user_uc = (ucontext_t*)regs->rsp; - debug("sigreturn thread %d regs: %p sp: %08lx " - "user_uc: %p gregs.rsp: %08lx gregs.rip: %08lx fpstate %p\n", - tcb->tp->tid, regs, regs->rsp, user_uc, - user_uc->uc_mcontext.gregs[REG_RSP], - user_uc->uc_mcontext.gregs[REG_RIP], - user_uc->uc_mcontext.fpregs); - if (handle_next_signal(user_uc)) { - fpstate_reset(); + xstate_reset(); return 0; } /* no more pending signals, return back to application */ - gregset_t* gregs = &user_uc->uc_mcontext.gregs; + greg_t* gregs = user_uc->uc_mcontext.gregs; /* rax will be restored as a return value of sigreturn, see below */ - regs->r15 = (*gregs)[REG_R15]; - regs->r14 = (*gregs)[REG_R14]; - regs->r13 = (*gregs)[REG_R13]; - regs->r12 = (*gregs)[REG_R12]; - regs->r11 = (*gregs)[REG_R11]; - regs->r10 = (*gregs)[REG_R10]; - regs->r9 = (*gregs)[REG_R9]; - regs->r8 = (*gregs)[REG_R8]; - regs->rcx = (*gregs)[REG_RCX]; - regs->rdx = (*gregs)[REG_RDX]; - regs->rsi = (*gregs)[REG_RSI]; - regs->rdi = (*gregs)[REG_RDI]; - regs->rbx = (*gregs)[REG_RBX]; - regs->rbp = (*gregs)[REG_RBP]; - regs->rflags = (*gregs)[REG_EFL]; - regs->rip = (*gregs)[REG_RIP]; - regs->rsp = (*gregs)[REG_RSP]; + regs->r15 = gregs[REG_R15]; + regs->r14 = gregs[REG_R14]; + regs->r13 = gregs[REG_R13]; + regs->r12 = gregs[REG_R12]; + regs->r11 = gregs[REG_R11]; + regs->r10 = gregs[REG_R10]; + regs->r9 = gregs[REG_R9]; + regs->r8 = gregs[REG_R8]; + regs->rcx = gregs[REG_RCX]; + regs->rdx = gregs[REG_RDX]; + regs->rsi = gregs[REG_RSI]; + regs->rdi = gregs[REG_RDI]; + regs->rbx = gregs[REG_RBX]; + regs->rbp = gregs[REG_RBP]; + regs->rflags = gregs[REG_EFL]; + regs->rip = gregs[REG_RIP]; + regs->rsp = gregs[REG_RSP]; - struct _libc_fpstate * user_fpstate = user_uc->uc_mcontext.fpregs; - fpstate_restore(user_fpstate); + struct _libc_fpstate* user_fpstate = user_uc->uc_mcontext.fpregs; + struct _libc_xregs_state* user_xstate = + container_of(user_fpstate, struct _libc_xregs_state, fpstate); + xstate_restore(user_xstate); - return (*gregs)[REG_RAX]; + return gregs[REG_RAX]; } int shim_do_sigprocmask(int how, const __sigset_t* set, __sigset_t* oldset) { diff --git a/LibOS/shim/src/syscallas.S b/LibOS/shim/src/syscallas.S index e904e14f..a155f5aa 100644 --- a/LibOS/shim/src/syscallas.S +++ b/LibOS/shim/src/syscallas.S @@ -60,7 +60,7 @@ syscalldb: .cfi_undefined %r11 # switch stack - movq %gs:(SHIM_TCB_OFFSET + SHIM_TCB_SYSCALL_STACK_HIGH), %rsp + movq %gs:(SHIM_TCB_OFFSET + SHIM_TCB_SYSCALL_STACK), %rsp # Create shim_regs struct on the stack. pushq %r11 # save old %rsp @@ -251,7 +251,7 @@ isundef: * small application stack, e.g. as in goroutine. * See illegal_upcall() @ shim_signal.c * - * argument: + * arguments: * %rcx: Instruction address to continue app execution after trapped * syscall instruction * %r11: rflags on entering syscall: unused as %rflags is preserved @@ -270,7 +270,7 @@ syscall_wrapper: .cfi_undefined %r11 # switch stack to the one allocated by LibOS - movq %gs:(SHIM_TCB_OFFSET + SHIM_TCB_SYSCALL_STACK_HIGH), %rsp + movq %gs:(SHIM_TCB_OFFSET + SHIM_TCB_SYSCALL_STACK), %rsp pushq %r11 .cfi_def_cfa %rsp, 0 .cfi_offset %rsp, 0 diff --git a/LibOS/shim/src/vdso/vdso.c b/LibOS/shim/src/vdso/vdso.c index c15efb42..053ef551 100644 --- a/LibOS/shim/src/vdso/vdso.c +++ b/LibOS/shim/src/vdso/vdso.c @@ -37,6 +37,12 @@ EXPORT_SYMBOL(shim_gettimeofday); EXPORT_SYMBOL(shim_time); EXPORT_SYMBOL(shim_getcpu); +/* + * the targeted function is called if and only if + * != NULL && shim_check_sigpending != * NULL. + * Without calling shim_check_sigpending, the emulated signal can be queued, but never be + * delivered. + */ static void (*shim_check_sigpending)(void) = NULL; EXPORT_SYMBOL(shim_check_sigpending);