X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/87067c7f16425837971f5c1b5fd0fc5338757377..49884d629ca5223693a9243c59b80b9c1a14a427:/lunaix-os/kernel/process/signal.c diff --git a/lunaix-os/kernel/process/signal.c b/lunaix-os/kernel/process/signal.c index a8ec239..6690f98 100644 --- a/lunaix-os/kernel/process/signal.c +++ b/lunaix-os/kernel/process/signal.c @@ -4,13 +4,27 @@ #include #include #include +#include #include +#include + +LOG_MODULE("SIG") + extern struct scheduler sched_ctx; /* kernel/sched.c */ #define UNMASKABLE (sigset(SIGKILL) | sigset(SIGTERM)) #define TERMSIG (sigset(SIGSEGV) | sigset(SIGINT) | UNMASKABLE) +#define CORE (sigset(SIGSEGV)) +#define within_kstack(addr) \ + (KERNEL_STACK <= (addr) && (addr) <= KERNEL_STACK_END) + +static inline void +signal_terminate(int errcode) +{ + terminate_proc(errcode | PEXITSIG); +} // Referenced in kernel/asm/x86/interrupt.S void* @@ -25,7 +39,7 @@ signal_dispatch() struct sigact* prev_working = psig->inprogress; sigset_t mask = psig->sig_mask | (prev_working ? prev_working->sa_mask : 0); - int sig_selected = 31 - __builtin_clz(psig->sig_pending & ~mask); + int sig_selected = 31 - clz(psig->sig_pending & ~mask); sigset_clear(psig->sig_pending, sig_selected); @@ -38,7 +52,7 @@ signal_dispatch() if (!action->sa_actor) { if (sigset_test(TERMSIG, sig_selected)) { - terminate_proc(sig_selected | PEXITSIG); + signal_terminate(sig_selected); schedule(); // never return } @@ -47,43 +61,19 @@ signal_dispatch() ptr_t ustack = __current->ustack_top; - if ((int)(ustack - USTACK_END) < (int)sizeof(struct proc_sig)) { + if ((int)(ustack - USR_STACK) < (int)sizeof(struct proc_sig)) { // 用户栈没有空间存放信号上下文 return 0; } struct proc_sig* sigframe = - (struct proc_sig*)((ustack - sizeof(struct proc_sig)) & ~0xf); - - /* - 这是一个相当恶心的坑。 - 问题是出在原本的sigframe->prev_context = __current->intr_ctx的上面 - 这个语句会被gcc在编译时,用更加高效的 rep movsl 来代替。 - - 由于我们采用按需分页,所以在很多情况下,用户栈实际被分配的空间不允许我们进行完整的 - 注入,而需要走page fault handler进行动态分页。 - - 竞态条件就出现在这里! - - 假若我们的__current->intr_ctx注入了一半,然后产生page-fault中断, - 那么这就会导致我们的__current->intr_ctx被这个page-fault中断导致的 - 上下文信息覆盖。那么当page-fault handler成功分配了一个页,返回, - 拷贝也就得以进行。遗憾的是,只不过这次拷贝的内容和前面的拷贝是没有任何的关系 - (因为此时的intr_ctx已经不是之前的intr_ctx了!) - 而这就会导致我们保存在信号上下文中的进程上下文信息不完整,从而在soft_iret时 - 触发#GP。 - - 解决办法就是先吧intr_ctx拷贝到一个静态分配的区域里,然后再注入到用户栈。 - */ - static volatile struct proc_sigstate __temp_save; - __temp_save.proc_regs = __current->intr_ctx; - memcpy(__temp_save.fxstate, __current->fxstate, 512); + (struct proc_sig*)((ustack - sizeof(struct proc_sig)) & ~0xf); sigframe->sig_num = sig_selected; - sigframe->sigact = action->sa_actor; sigframe->sighand = action->sa_handler; - sigframe->prev_context = __temp_save; + + sigframe->saved_ictx = __current->intr_ctx; action->prev = prev_working; psig->inprogress = action; @@ -91,6 +81,12 @@ signal_dispatch() return sigframe; } +void +proc_clear_signal(struct proc_info* proc) +{ + memset(&proc->sigctx, 0, sizeof(proc->sigctx)); +} + void proc_setsignal(struct proc_info* proc, int signum) { @@ -115,7 +111,7 @@ signal_send(pid_t pid, int signum) } else if (!pid) { proc = __current; goto send_grp; - } else if (pid < -1) { + } else if (pid < 0) { proc = get_process(-pid); goto send_grp; } else { @@ -125,7 +121,7 @@ signal_send(pid_t pid, int signum) return -1; } -send_grp: +send_grp: ; struct proc_info *pos, *n; llist_for_each(pos, n, &proc->grp_member, grp_member) { @@ -135,7 +131,7 @@ send_grp: } send_single: - if (PROC_TERMINATED(proc->state)) { + if (proc_terminated(proc)) { __current->k_status = EINVAL; return -1; } @@ -148,8 +144,7 @@ send_single: __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx) { - memcpy(__current->fxstate, sig_ctx->prev_context.fxstate, 512); - __current->intr_ctx = sig_ctx->prev_context.proc_regs; + __current->intr_ctx = sig_ctx->saved_ictx; struct sigact* current = __current->sigctx.inprogress; if (current) { @@ -159,20 +154,32 @@ __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx) __current->sigctx.inprogress = NULL; } + if (proc_terminated(__current)) { + __current->exit_code |= PEXITSIG; + } else if (sigset_test(CORE, sig_ctx->sig_num)) { + signal_terminate(sig_ctx->sig_num); + } + + ptr_t ictx = (ptr_t)__current->intr_ctx; + + /* + Ensure our restored context is within kernel stack + + This prevent user to forge their own context such that arbitrary code + can be executed as supervisor level + */ + if (!within_kstack(ictx)) { + signal_terminate(SIGSEGV); + } + schedule(); // never reach! return 0; } -__DEFINE_LXSYSCALL3(int, - sigprocmask, - int, - how, - const sigset_t, - *set, - sigset_t, - *oldset) +__DEFINE_LXSYSCALL3( + int, sigprocmask, int, how, const sigset_t, *set, sigset_t, *oldset) { struct sighail* sh = &__current->sigctx; *oldset = sh->sig_mask;