refactor: add a async read/write variant to device ops, with allow async io to be...
[lunaix-os.git] / lunaix-os / kernel / process / signal.c
index a8ec2397eaf3b98d4d6839892fe3184b8fe132cb..8b66663a676faa743390e7be02aa2f359c2c65a6 100644 (file)
@@ -4,13 +4,27 @@
 #include <lunaix/spike.h>
 #include <lunaix/status.h>
 #include <lunaix/syscall.h>
+#include <lunaix/syslog.h>
 
 #include <klibc/string.h>
 
+#include <sys/mm/mempart.h>
+
+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 {
@@ -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;