/* vim: tabstop=4 shiftwidth=4 noexpandtab * This file is part of ToaruOS and is released under the terms * of the NCSA / University of Illinois License - see LICENSE.md * Copyright (C) 2012-2018 K. Lange * * Signal Handling */ #include #include #include void enter_signal_handler(uintptr_t location, int signum, uintptr_t stack) { IRQ_OFF; uintptr_t ebp = current_process->syscall_registers->ebp; uintptr_t esp = current_process->syscall_registers->useresp; asm volatile( "mov %2, %%esp\n" "pushl %4\n" "pushl %3\n" "pushl %1\n" /* argument count */ "pushl $" STRSTR(SIGNAL_RETURN) "\n" "mov $0x23, %%ax\n" /* Segment selector */ "mov %%ax, %%ds\n" "mov %%ax, %%es\n" "mov %%ax, %%fs\n" "mov %%ax, %%gs\n" "mov %%esp, %%eax\n" /* Stack -> EAX */ "pushl $0x23\n" /* Segment selector again */ "pushl %%eax\n" "pushf\n" /* Push flags */ "popl %%eax\n" /* Fix the Interrupt flag */ "orl $0x200, %%eax\n" "pushl %%eax\n" "pushl $0x1B\n" "pushl %0\n" /* Push the entry point */ "iret\n" : : "m"(location), "m"(signum), "r"(stack), "m"(ebp), "m"(esp) : "%ax", "%esp", "%eax"); debug_print(CRITICAL, "Failed to jump to signal handler!"); } static spin_lock_t sig_lock; static spin_lock_t sig_lock_b; char isdeadly[] = { 0, /* 0? */ 1, /* SIGHUP */ 1, /* SIGINT */ 2, /* SIGQUIT */ 2, /* SIGILL */ 2, /* SIGTRAP */ 2, /* SIGABRT */ 2, /* SIGEMT */ 2, /* SIGFPE */ 1, /* SIGKILL */ 2, /* SIGBUS */ 2, /* SIGSEGV */ 2, /* SIGSYS */ 1, /* SIGPIPE */ 1, /* SIGALRM */ 1, /* SIGTERM */ 1, /* SIGUSR1 */ 1, /* SIGUSR2 */ 0, /* SIGCHLD */ 0, /* SIGPWR */ 0, /* SIGWINCH */ 0, /* SIGURG */ 0, /* SIGPOLL */ 3, /* SIGSTOP */ 3, /* SIGTSTP */ 4, /* SIGCONT */ 3, /* SIGTTIN */ 3, /* SIGTTOUT */ 1, /* SIGVTALRM */ 1, /* SIGPROF */ 2, /* SIGXCPU */ 2, /* SIGXFSZ */ 0, /* SIGWAITING */ 1, /* SIGDIAF */ 0, /* SIGHATE */ 0, /* SIGWINEVENT*/ 0, /* SIGCAT */ }; void handle_signal(process_t * proc, signal_t * sig) { uintptr_t handler = sig->handler; uintptr_t signum = sig->signum; free(sig); if (proc->finished) { return; } if (signum == 0 || signum >= NUMSIGNALS) { /* Ignore */ return; } if (!handler) { char dowhat = isdeadly[signum]; if (dowhat == 1 || dowhat == 2) { debug_print(WARNING, "Process %d killed by unhandled signal (%d)", proc->id, signum); kexit(((128 + signum) << 8) | signum); __builtin_unreachable(); } else if (dowhat == 3) { debug_print(WARNING, "suspending pid %d", proc->id); current_process->suspended = 1; current_process->status = 0x7F; process_t * parent = process_get_parent((process_t *)current_process); if (parent && !parent->finished) { wakeup_queue(parent->wait_queue); } switch_task(0); } else if (dowhat == 4) { switch_task(1); return; } else { debug_print(WARNING, "Ignoring signal %d by default in pid %d", signum, proc->id); } /* XXX dowhat == 2: should dump core */ /* XXX dowhat == 3: stop */ return; } if (handler == 1) /* Ignore */ { return; } debug_print(NOTICE, "handling signal in process %d (%d) (0x%x)", proc->id, signum, handler); uintptr_t stack = 0xFFFF0000; if (proc->syscall_registers->useresp < 0x10000100) { stack = proc->image.user_stack; } else { stack = proc->syscall_registers->useresp; } /* Not marked as ignored, must call signal */ enter_signal_handler(handler, signum, stack); } list_t * rets_from_sig; void return_from_signal_handler(void) { #if 0 debug_print(ERROR, "Return From Signal for process %d", current_process->id); #endif if (__builtin_expect(!rets_from_sig, 0)) { rets_from_sig = list_create(); } spin_lock(sig_lock); list_insert(rets_from_sig, (process_t *)current_process); spin_unlock(sig_lock); switch_next(); } void fix_signal_stacks(void) { uint8_t redo_me = 0; if (rets_from_sig) { spin_lock(sig_lock_b); while (rets_from_sig->head) { spin_lock(sig_lock); node_t * n = list_dequeue(rets_from_sig); spin_unlock(sig_lock); if (!n) { continue; } process_t * p = n->value; free(n); if (p == current_process) { redo_me = 1; continue; } p->thread.esp = p->signal_state.esp; p->thread.eip = p->signal_state.eip; p->thread.ebp = p->signal_state.ebp; if (!p->signal_kstack) { debug_print(ERROR, "Cannot restore signal stack for pid=%d - unset?", p->id); } else { debug_print(ERROR, "Restoring signal stack for pid=%d", p->id); memcpy((void *)(p->image.stack - KERNEL_STACK_SIZE), p->signal_kstack, KERNEL_STACK_SIZE); free(p->signal_kstack); p->signal_kstack = NULL; } make_process_ready(p); } spin_unlock(sig_lock_b); } if (redo_me) { spin_lock(sig_lock); list_insert(rets_from_sig, (process_t *)current_process); spin_unlock(sig_lock); switch_next(); } } int send_signal(pid_t process, uint32_t signal, int force_root) { process_t * receiver = process_from_pid(process); if (!receiver) { /* Invalid pid */ return -ESRCH; } if (!force_root && receiver->user != current_process->user && current_process->user != USER_ROOT_UID) { if (!(signal == SIGCONT && receiver->session == current_process->session)) { return -EPERM; } } if (signal > NUMSIGNALS) { /* Invalid signal */ return -EINVAL; } if (receiver->finished) { /* Can't send signals to finished processes */ return -EINVAL; } if (!receiver->signals.functions[signal] && !isdeadly[signal]) { /* If we're blocking a signal and it's not going to kill us, don't deliver it */ return 0; } if (isdeadly[signal] == 4) { if (!receiver->suspended) { return -EINVAL; } else { debug_print(WARNING, "Resuming pid %d from suspend", receiver->id); receiver->suspended = 0; receiver->status = 0; } } /* Append signal to list */ signal_t * sig = malloc(sizeof(signal_t)); sig->handler = (uintptr_t)receiver->signals.functions[signal]; sig->signum = signal; memset(&sig->registers_before, 0x00, sizeof(regs_t)); if (receiver->node_waits) { process_awaken_from_fswait(receiver, -1); } if (!process_is_ready(receiver)) { make_process_ready(receiver); } list_insert(receiver->signal_queue, sig); if (receiver == current_process) { /* Forces us to be rescheduled and enter signal handler */ if (receiver->signal_kstack) { switch_next(); } else { switch_task(0); } } return 0; } int group_send_signal(int group, uint32_t signal, int force_root) { int kill_self = 0; int killed_something = 0; debug_print(WARNING, "killing group %d", group); foreach(node, process_list) { process_t * proc = node->value; debug_print(WARNING, "examining %d %d %d", proc->id, proc->job, proc->group); if (proc->group == proc->id && proc->job == group) { /* Only thread group leaders */ debug_print(WARNING, "killing %d", proc->group); if (proc->group == current_process->group) { kill_self = 1; } else { if (send_signal(proc->group, signal, force_root) == 0) { killed_something = 1; } } } } if (kill_self) { if (send_signal(current_process->group, signal, force_root) == 0) { killed_something = 1; } } return !!killed_something; }