/* * Copyright (c) 2011-2016, 2018, 2021 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * signal.cpp * Asynchronous user-space thread interruption. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Sortix { sigset_t default_ignored_signals; sigset_t default_stop_signals; sigset_t unblockable_signals; // A per-cpu value whether a signal is pending in the running task. extern "C" { volatile unsigned long asm_signal_is_pending = 0; } static void UpdatePendingSignals(Thread* thread) // thread->process->signal_lock held { struct sigaction* signal_actions = thread->process->signal_actions; // Determine which signals wouldn't be ignored if received. sigset_t handled_signals; sigemptyset(&handled_signals); for ( int i = 1; i < SIG_MAX_NUM; i++ ) { if ( signal_actions[i].sa_handler == SIG_IGN ) continue; if ( signal_actions[i].sa_handler == SIG_DFL && sigismember(&default_ignored_signals, i) ) continue; // TODO: A process that is a member of an orphaned process group shall // not be allowed to stop in response to the SIGTSTP, SIGTTIN, or // SIGTTOU signals. In cases where delivery of one of these // signals would stop such a process, the signal shall be // discarded. if ( /* is member of an orphaned process group */ false && signal_actions[i].sa_handler == SIG_DFL && sigismember(&default_stop_signals, i) ) continue; sigaddset(&handled_signals, i); } // TODO: Handle that signals can be pending process-wide! // Discard all requested signals that would be ignored if delivered. sigandset(&thread->signal_pending, &thread->signal_pending, &handled_signals); // Determine which signals are not blocked. sigset_t permitted_signals; signotset(&permitted_signals, &thread->signal_mask); sigorset(&permitted_signals, &permitted_signals, &unblockable_signals); // Determine which signals can currently be delivered to this thread. sigset_t deliverable_signals; sigandset(&deliverable_signals, &permitted_signals, &thread->signal_pending); // Determine whether any signals can be delivered. unsigned long is_pending = !sigisemptyset(&deliverable_signals) ? 1 : 0; if ( thread->force_no_signals ) is_pending = 0; // Store whether a signal is pending in the virtual register. if ( thread == CurrentThread() ) asm_signal_is_pending = is_pending; else Scheduler::SetSignalPending(thread, is_pending); } void Thread::DoUpdatePendingSignal() { ScopedLock lock(&process->signal_lock); UpdatePendingSignals(this); } int sys_sigaction(int signum, const struct sigaction* user_newact, struct sigaction* user_oldact) { if ( signum < 0 || signum == 0 /* null signal */ || SIG_MAX_NUM <= signum ) return errno = EINVAL, -1; Process* process = CurrentProcess(); ScopedLock lock(&process->signal_lock); struct sigaction* kact = &process->signal_actions[signum]; // Let the caller know the previous action. if ( user_oldact ) { if ( !CopyToUser(user_oldact, kact, sizeof(struct sigaction)) ) return -1; } // Retrieve and validate the new signal action. if ( user_newact ) { struct sigaction newact; if ( !CopyFromUser(&newact, user_newact, sizeof(struct sigaction)) ) return -1; if ( newact.sa_flags & ~__SA_SUPPORTED_FLAGS ) return errno = EINVAL, -1; if ( newact.sa_handler == SIG_ERR ) return errno = EINVAL, -1; memcpy(kact, &newact, sizeof(struct sigaction)); // Signals may become discarded because of the new handler. ScopedLock threads_lock(&process->threadlock); for ( Thread* t = process->firstthread; t; t = t->nextsibling ) UpdatePendingSignals(t); } return 0; } int sys_sigaltstack(const stack_t* user_newstack, stack_t* user_oldstack) { Thread* thread = CurrentThread(); if ( user_oldstack ) { if ( !CopyToUser(user_oldstack, &thread->signal_stack, sizeof(stack_t)) ) return -1; } if ( user_newstack ) { stack_t newstack; if ( !CopyFromUser(&newstack, user_newstack, sizeof(stack_t)) ) return -1; if ( newstack.ss_flags & ~__SS_SUPPORTED_FLAGS ) return errno = EINVAL, -1; memcpy(&thread->signal_stack, &newstack, sizeof(stack_t)); } return 0; } int sys_sigpending(sigset_t* set) { Process* process = CurrentProcess(); Thread* thread = CurrentThread(); ScopedLock lock(&process->signal_lock); // TODO: What about process-wide signals? return CopyToUser(set, &thread->signal_pending, sizeof(sigset_t)) ? 0 : -1; } namespace Signal { void UpdateMask(int how, const sigset_t* set, sigset_t* oldset) { Process* process = CurrentProcess(); Thread* thread = CurrentThread(); // TODO: Signal masks are a per-thread property, perhaps this should be // locked in another manner? ScopedLock lock(&process->signal_lock); // Let the caller know the previous signal mask. if ( oldset ) memcpy(oldset, &thread->signal_mask, sizeof(sigset_t)); // Update the current signal mask according to how. if ( set ) { switch ( how ) { case SIG_BLOCK: sigorset(&thread->signal_mask, &thread->signal_mask, set); break; case SIG_UNBLOCK: { sigset_t notset; signotset(¬set, set); sigandset(&thread->signal_mask, &thread->signal_mask, ¬set); break; } case SIG_SETMASK: memcpy(&thread->signal_mask, set, sizeof(sigset_t)); break; }; UpdatePendingSignals(thread); } } } // namespace Signal int sys_sigprocmask(int how, const sigset_t* user_set, sigset_t* user_oldset) { if ( how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK ) return errno = EINVAL, -1; sigset_t set, oldset; if ( user_set && !CopyFromUser(&set, user_set, sizeof(sigset_t)) ) return -1; Signal::UpdateMask( how, user_set ? &set : NULL, user_oldset ? &oldset : NULL); if ( user_oldset && !CopyToUser(user_oldset, &oldset, sizeof(sigset_t)) ) return -1; return 0; } int sys_sigsuspend(const sigset_t* set) { Process* process = CurrentProcess(); Thread* thread = CurrentThread(); sigset_t old_signal_mask; sigemptyset(&old_signal_mask); ScopedLock lock(&process->signal_lock); // Only accept signals from the user-provided set if given. if ( set ) { sigset_t new_signal_mask; if ( !CopyFromUser(&new_signal_mask, set, sizeof(sigset_t)) ) return -1; memcpy(&old_signal_mask, &thread->signal_mask, sizeof(sigset_t)); memcpy(&thread->signal_mask, &new_signal_mask, sizeof(sigset_t)); UpdatePendingSignals(thread); } // Wait for a signal to happen or otherwise never halt. kthread_cond_t never_triggered = KTHREAD_COND_INITIALIZER; while ( !Signal::IsPending() ) kthread_cond_wait_signal(&never_triggered, &process->signal_lock); // The pending signal might only be pending with the temporary signal mask, // so don't restore it. Instead ask for the real signal mask to be restored // after the signal has been processed. if ( set ) { thread->has_saved_signal_mask = true; memcpy(&thread->saved_signal_mask, &old_signal_mask, sizeof(sigset_t)); } // The system call never halts or it halts because a signal interrupted it. return errno = EINTR, -1; } int sys_kill(pid_t pid, int signum) { // Protect the kernel process. if ( !pid ) return errno = EPERM, -1; // TODO: Implement that pid == -1 means all processes! bool process_group = pid < 0 ? (pid = -pid, true) : false; ScopedLock lock(&process_family_lock); Process* process = CurrentProcess()->GetPTable()->Get(pid); if ( !process ) return errno = ESRCH, -1; // TODO: Protect init? // TODO: Check for permission. // TODO: Check for zombies. if ( process_group ) { if ( !process->DeliverGroupSignal(signum) && errno != ESIGPENDING ) return -1; return errno = 0, 0; } if ( !process->DeliverSignal(signum) && errno != ESIGPENDING ) return -1; return errno = 0, 0; } bool Process::DeliverGroupSignal(int signum) // process_family_lock held { if ( !groupfirst ) return errno = ESRCH, false; for ( Process* iter = groupfirst; iter; iter = iter->groupnext ) { int saved_errno = errno; if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING ) { // This is not currently an error condition. } errno = saved_errno; } return true; } bool Process::DeliverSessionSignal(int signum) // process_family_lock held { if ( !sessionfirst ) return errno = ESRCH, false; for ( Process* iter = sessionfirst; iter; iter = iter->sessionnext ) { int saved_errno = errno; if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING ) { // This is not currently an error condition. } errno = saved_errno; } return true; } bool Process::DeliverSignal(int signum) { ScopedLock lock(&threadlock); if ( !firstthread ) return errno = EINIT, false; // Broadcast particular signals to all the threads in the process. if ( signum == SIGCONT || signum == SIGSTOP || signum == SIGKILL ) { int saved_errno = errno; for ( Thread* t = firstthread; t; t = t->nextsibling ) { if ( !t->DeliverSignal(signum) && errno != ESIGPENDING ) { // This is not currently an error condition. } } errno = saved_errno; return true; } // Route the signal to a suitable thread that accepts it. // TODO: This isn't how signals should be routed to a particular thread. if ( CurrentThread()->process == this ) return CurrentThread()->DeliverSignal(signum); return firstthread->DeliverSignal(signum); } int sys_raise(int signum) { if ( !CurrentThread()->DeliverSignal(signum) && errno != ESIGPENDING ) return -1; return errno = 0, 0; } bool Thread::DeliverSignal(int signum) { ScopedLock lock(&process->signal_lock); return DeliverSignalUnlocked(signum); } bool Thread::DeliverSignalUnlocked(int signum) // thread->process->signal_lock held { if ( signum <= 0 || SIG_MAX_NUM <= signum ) return errno = EINVAL, false; // Discard the null signal, which does error checking, but doesn't actually // deliver a signal to the process or thread. if ( signum == 0 ) return true; if ( sigismember(&signal_pending, signum) ) return errno = ESIGPENDING, false; sigaddset(&signal_pending, signum); if ( signum == SIGSTOP || signum == SIGTSTP || signum == SIGTTIN || signum == SIGTTOU ) sigdelset(&signal_pending, SIGCONT); if ( signum == SIGCONT ) { sigdelset(&signal_pending, SIGSTOP); sigdelset(&signal_pending, SIGTSTP); sigdelset(&signal_pending, SIGTTIN); sigdelset(&signal_pending, SIGTTOU); } UpdatePendingSignals(this); return true; } static int PickImportantSignal(const sigset_t* set) { if ( sigismember(set, SIGKILL) ) return SIGKILL; if ( sigismember(set, SIGSTOP) ) return SIGSTOP; for ( int i = 1; i < SIG_MAX_NUM; i++ ) if ( sigismember(set, i) ) return i; return 0; } static void EncodeMachineContext(mcontext_t* mctx, const struct thread_registers* regs, const struct interrupt_context* intctx) { memset(mctx, 0, sizeof(*mctx)); #if defined(__i386__) // TODO: REG_GS // TODO: REG_FS // TODO: REG_ES // TODO: REG_DS mctx->gregs[REG_EDI] = regs->edi; mctx->gregs[REG_ESI] = regs->esi; mctx->gregs[REG_EBP] = regs->ebp; mctx->gregs[REG_ESP] = regs->esp; mctx->gregs[REG_EBX] = regs->ebx; mctx->gregs[REG_EDX] = regs->edx; mctx->gregs[REG_ECX] = regs->ecx; mctx->gregs[REG_EAX] = regs->eax; mctx->gregs[REG_EIP] = regs->eip; // TODO: REG_CS mctx->gregs[REG_EFL] = regs->eflags & 0x0000FFFF; mctx->gregs[REG_CR2] = intctx->cr2; // TODO: REG_SS memcpy(mctx->fpuenv, regs->fpuenv, 512); #elif defined(__x86_64__) mctx->gregs[REG_R8] = regs->r8; mctx->gregs[REG_R9] = regs->r9; mctx->gregs[REG_R10] = regs->r10; mctx->gregs[REG_R11] = regs->r11; mctx->gregs[REG_R12] = regs->r12; mctx->gregs[REG_R13] = regs->r13; mctx->gregs[REG_R14] = regs->r14; mctx->gregs[REG_R15] = regs->r15; mctx->gregs[REG_RDI] = regs->rdi; mctx->gregs[REG_RSI] = regs->rsi; mctx->gregs[REG_RBP] = regs->rbp; mctx->gregs[REG_RBX] = regs->rbx; mctx->gregs[REG_RDX] = regs->rdx; mctx->gregs[REG_RAX] = regs->rax; mctx->gregs[REG_RCX] = regs->rcx; mctx->gregs[REG_RSP] = regs->rsp; mctx->gregs[REG_RIP] = regs->rip; mctx->gregs[REG_EFL] = regs->rflags & 0x000000000000FFFF; // TODO: REG_CSGSFS. mctx->gregs[REG_CR2] = intctx->cr2; mctx->gregs[REG_FSBASE] = 0x0; mctx->gregs[REG_GSBASE] = 0x0; memcpy(mctx->fpuenv, regs->fpuenv, 512); #else #error "You need to implement conversion to mcontext" #endif } static void DecodeMachineContext(const mcontext_t* mctx, struct thread_registers* regs) { #if defined(__i386__) || defined(__x86_64__) unsigned long user_flags = FLAGS_CARRY | FLAGS_PARITY | FLAGS_AUX | FLAGS_ZERO | FLAGS_SIGN | FLAGS_DIRECTION | FLAGS_OVERFLOW; #endif #if defined(__i386__) regs->edi = mctx->gregs[REG_EDI]; regs->esi = mctx->gregs[REG_ESI]; regs->ebp = mctx->gregs[REG_EBP]; regs->esp = mctx->gregs[REG_ESP]; regs->ebx = mctx->gregs[REG_EBX]; regs->edx = mctx->gregs[REG_EDX]; regs->ecx = mctx->gregs[REG_ECX]; regs->eax = mctx->gregs[REG_EAX]; regs->eip = mctx->gregs[REG_EIP]; regs->eflags &= ~user_flags; regs->eflags |= mctx->gregs[REG_EFL] & user_flags; memcpy(regs->fpuenv, mctx->fpuenv, 512); #elif defined(__x86_64__) regs->r8 = mctx->gregs[REG_R8]; regs->r9 = mctx->gregs[REG_R9]; regs->r10 = mctx->gregs[REG_R10]; regs->r11 = mctx->gregs[REG_R11]; regs->r12 = mctx->gregs[REG_R12]; regs->r13 = mctx->gregs[REG_R13]; regs->r14 = mctx->gregs[REG_R14]; regs->r15 = mctx->gregs[REG_R15]; regs->rdi = mctx->gregs[REG_RDI]; regs->rsi = mctx->gregs[REG_RSI]; regs->rbp = mctx->gregs[REG_RBP]; regs->rbx = mctx->gregs[REG_RBX]; regs->rdx = mctx->gregs[REG_RDX]; regs->rax = mctx->gregs[REG_RAX]; regs->rcx = mctx->gregs[REG_RCX]; regs->rsp = mctx->gregs[REG_RSP]; regs->rip = mctx->gregs[REG_RIP]; regs->rflags &= ~user_flags; regs->rflags |= mctx->gregs[REG_EFL] & user_flags; memcpy(regs->fpuenv, mctx->fpuenv, 512); #else #error "You need to implement conversion to mcontext" #endif } #if defined(__i386__) struct stack_frame { uintptr_t misalignment[3]; uintptr_t sigreturn; int signum_param; siginfo_t* siginfo_param; ucontext_t* ucontext_param; void* cookie_param; uintptr_t canary; ucontext_t ucontext; siginfo_t siginfo; }; #elif defined(__x86_64__) struct stack_frame { uintptr_t misalignment[1]; uintptr_t sigreturn; uintptr_t canary; ucontext_t ucontext; siginfo_t siginfo; }; #else #error "You need to implement struct stack_frame" #endif void Thread::HandleSignal(struct interrupt_context* intctx) { assert(Interrupt::IsEnabled()); assert(this == CurrentThread()); ScopedLock lock(&process->signal_lock); retry_another_signal: // Determine which signals are not blocked. sigset_t permitted_signals; signotset(&permitted_signals, &signal_mask); sigorset(&permitted_signals, &permitted_signals, &unblockable_signals); // Determine which signals can currently be delivered to this thread. sigset_t deliverable_signals; sigandset(&deliverable_signals, &permitted_signals, &signal_pending); // Decide which signal to deliver to the thread. int signum = PickImportantSignal(&deliverable_signals); if ( !signum ) { if ( has_saved_signal_mask ) { memcpy(&signal_mask, &saved_signal_mask, sizeof(sigset_t)); has_saved_signal_mask = false; UpdatePendingSignals(this); goto retry_another_signal; } return; } // Unmark the selected signal as pending. sigdelset(&signal_pending, signum); UpdatePendingSignals(this); intctx->signal_pending = asm_signal_is_pending; // Destroy the current thread if the signal is critical. if ( signum == SIGKILL ) { lock.Reset(); kthread_exit(); } struct sigaction* action = &process->signal_actions[signum]; // Stop the current thread upon receipt of a stop signal that isn't handled // or cannot be handled (SIGSTOP). if ( (action->sa_handler == SIG_DFL && sigismember(&default_stop_signals, signum) ) || signum == SIGSTOP ) { Log::PrintF("%s:%u: `%s' FIXME SIGSTOP\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); // TODO: Stop the current process. // TODO: Deliver SIGCHLD to the parent except if SA_NOCLDSTOP is set in // the parent's SIGCHLD sigaction. // TODO: SIGCHLD should not be delivered until all the threads in the // process has received SIGSTOP and stopped? // TODO: SIGKILL must still be deliverable to a stopped process. } // Resume the current thread upon receipt of SIGCONT. if ( signum == SIGCONT ) { Log::PrintF("%s:%u: `%s' FIXME SIGCONT\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); // TODO: Resume the current process. // TODO: Can SIGCONT be masked? // TODO: Can SIGCONT be handled? // TODO: Can SIGCONT be ignored? // TODO: Deliver SIGCHLD to the parent except if SA_NOCLDSTOP is set in // the parent's SIGCHLD sigaction. } // Signals that would be ignored are already filtered away at this point. assert(action->sa_handler != SIG_IGN); assert(action->sa_handler != SIG_DFL || !sigismember(&default_ignored_signals, signum)); // The default action must be to terminate the process. Signals that are // ignored by default got discarded earlier. If execve() failed, sigreturn // may be NULL and the process isn't able to properly process signals. if ( action->sa_handler == SIG_DFL || !process->sigreturn ) { kthread_mutex_unlock(&process->signal_lock); process->ExitThroughSignal(signum); kthread_mutex_lock(&process->signal_lock); goto retry_another_signal; } // At this point we have to attempt to invoke the user-space signal handler, // which will then return control to us through sigreturn. However, we can't // save the kernel state because 1) we can't trust the user-space stack 2) // we can't rely on the kernel stack being intact as the signal handler may // invoke system calls. For those reasons, we'll have to modify the saved // registers so they restore a user-space state. We can do this because // threads in the kernel cannot be delivered signals except when returning // from a system call, so we'll simply save the state that would have been // returned to user-space had no signal occured. if ( !InUserspace(intctx) ) { #if defined(__i386__) uint32_t* params = (uint32_t*) intctx->ebx; intctx->eip = params[0]; intctx->eflags = params[2]; intctx->esp = params[3]; intctx->cs = UCS | URPL; intctx->ds = UDS | URPL; intctx->ss = UDS | URPL; intctx->ebx = 0; #elif defined(__x86_64__) intctx->rip = intctx->rdi; intctx->rflags = intctx->rsi; intctx->rsp = intctx->r8; intctx->cs = UCS | URPL; intctx->ds = UDS | URPL; intctx->ss = UDS | URPL; intctx->rdi = 0; intctx->rsi = 0; intctx->r8 = 0; #else #error "You may need to fix the registers" #endif } struct thread_registers stopped_regs; Scheduler::SaveInterruptedContext(intctx, &stopped_regs); sigset_t new_signal_mask; memcpy(&new_signal_mask, &action->sa_mask, sizeof(sigset_t)); sigorset(&new_signal_mask, &new_signal_mask, &signal_mask); // Prevent signals from interrupting themselves by default. if ( !(action->sa_flags & SA_NODEFER) ) sigaddset(&new_signal_mask, signum); // Determine whether we use an alternate signal stack. bool signal_uses_altstack = action->sa_flags & SA_ONSTACK; bool usable_altstack = !(signal_stack.ss_flags & (SS_DISABLE | SS_ONSTACK)); bool use_altstack = signal_uses_altstack && usable_altstack; // Determine which signal stack to use and what to save. stack_t old_signal_stack, new_signal_stack; uintptr_t stack_location; if ( use_altstack ) { old_signal_stack = signal_stack; new_signal_stack = signal_stack; new_signal_stack.ss_flags |= SS_ONSTACK; #if defined(__i386__) || defined(__x86_64__) stack_location = (uintptr_t) signal_stack.ss_sp + signal_stack.ss_size; #else #error "You need to implement getting the alternate stack pointer" #endif } else { old_signal_stack.ss_sp = NULL; old_signal_stack.ss_flags = SS_DISABLE; old_signal_stack.ss_size = 0; new_signal_stack = signal_stack; #if defined(__i386__) stack_location = (uintptr_t) stopped_regs.esp; #elif defined(__x86_64__) stack_location = (uintptr_t) stopped_regs.rsp; #else #error "You need to implement getting the user-space stack pointer" #endif } struct thread_registers handler_regs; memcpy(&handler_regs, &stopped_regs, sizeof(handler_regs)); struct stack_frame stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); void* handler_ptr = action->sa_flags & SA_COOKIE ? (void*) action->sa_sigaction_cookie : action->sa_flags & SA_SIGINFO ? (void*) action->sa_sigaction : (void*) action->sa_handler; #if defined(__i386__) stack_location -= sizeof(stack_frame); stack_location &= ~(16UL-1UL); /* 16-byte align */ struct stack_frame* stack = (struct stack_frame*) stack_location; stack_frame.sigreturn = (uintptr_t) process->sigreturn; stack_frame.signum_param = signum; stack_frame.siginfo_param = &stack->siginfo; stack_frame.ucontext_param = &stack->ucontext; stack_frame.cookie_param = action->sa_cookie; handler_regs.esp = (unsigned long) stack + sizeof(stack->misalignment); handler_regs.eip = (unsigned long) handler_ptr; handler_regs.eflags &= ~FLAGS_DIRECTION; #elif defined(__x86_64__) stack_location -= 128; /* Red zone. */ stack_location -= sizeof(stack_frame); stack_location &= ~(16UL-1UL); /* 16-byte align */ struct stack_frame* stack = (struct stack_frame*) stack_location; stack_frame.sigreturn = (uintptr_t) process->sigreturn; handler_regs.rdi = (unsigned long) signum; handler_regs.rsi = (unsigned long) &stack->siginfo; handler_regs.rdx = (unsigned long) &stack->ucontext; handler_regs.rcx = (unsigned long) action->sa_cookie; handler_regs.rsp = (unsigned long) stack + sizeof(stack->misalignment); handler_regs.rip = (unsigned long) handler_ptr; handler_regs.rflags &= ~FLAGS_DIRECTION; #else #error "You need to format the stack frame" #endif // Store a canary so it can later be verified this is a real signal being // returned from. if ( signal_count == 0 ) arc4random_buf(&signal_canary, sizeof(signal_canary)); stack_frame.canary = signal_canary ^ (uintptr_t) stack; // Format the siginfo into the stack frame. stack_frame.siginfo.si_signo = signum; #if defined(__i386__) || defined(__x86_64__) // TODO: Is this cr2 value trustworthy? I don't think it is. if ( signum == SIGSEGV ) stack_frame.siginfo.si_addr = (void*) intctx->cr2; #else #warning "You need to tell user-space where it crashed" #endif // Format the ucontext into the stack frame. stack_frame.ucontext.uc_link = NULL; if ( has_saved_signal_mask ) { // If a system call temporarily set another signal mask, it wants us to // deliver signals for that temporary signal masks, however we must // restore the original saved signal mask in that case. memcpy(&stack_frame.ucontext.uc_sigmask, &saved_signal_mask, sizeof(saved_signal_mask)); // 'has_saved_signal_mask' is set to false below when it's actually // delivered. This handles the case where the signal delivery fails and // and instead turns into another (we still want to restore the right // saved mask in that case). } else memcpy(&stack_frame.ucontext.uc_sigmask, &signal_mask, sizeof(signal_mask)); memcpy(&stack_frame.ucontext.uc_stack, &signal_stack, sizeof(signal_stack)); EncodeMachineContext(&stack_frame.ucontext.uc_mcontext, &stopped_regs, intctx); if ( !CopyToUser(stack, &stack_frame, sizeof(stack_frame)) ) { // Self-destruct if we crashed during delivering the crash signal. if ( signum == SIGSEGV ) { kthread_mutex_unlock(&process->signal_lock); process->ExitThroughSignal(signum); kthread_mutex_lock(&process->signal_lock); goto retry_another_signal; } // Deliver SIGSEGV if we could not deliver the signal on the stack. // TODO: Is it possible to block SIGSEGV here? kthread_mutex_unlock(&process->signal_lock); DeliverSignal(SIGSEGV); kthread_mutex_lock(&process->signal_lock); goto retry_another_signal; } // Update the current signal mask. UpdatePendingSignals isn't called because // sa_mask was or'd onto the current signal mask, only blocking signals, so // nothing new can be delivered. memcpy(&signal_mask, &new_signal_mask, sizeof(sigset_t)); // Update the current alternate signal stack. signal_stack = new_signal_stack; // Update the current registers. Scheduler::LoadInterruptedContext(intctx, &handler_regs); // Reset the signal handler if this signal handler is once only. if ( action->sa_flags & SA_RESETHAND ) { // POSIX mandates SA_RESETHAND is silently ignored for these signals. if ( signum != SIGILL && signum != SIGTRAP ) { action->sa_flags &= ~SA_RESETHAND; action->sa_handler = SIG_DFL; } } // Know for sure when there's no signal still being handled. This is an // over-approximation as programs may do absolutely awful things such as // longjmp(3)ing out of signal handlers, so each delivered signal does not // nessesarily mean a sigreturn. This will work correctly in reasonable // programs though and will harden those programs. Remember the stack frame // as well for verification if there is no recursive signal handling. if ( signal_count != SIZE_MAX ) signal_count++; if ( (signal_single = signal_count == 1) ) signal_single_frame = (uintptr_t) stack; // If there was a saved signal mask, it will be restored once the signal // handler complete, so forget it was ever saved. has_saved_signal_mask = false; // Run the signal handler by returning to user-space. return; } void Thread::HandleSigreturn(struct interrupt_context* intctx) { assert(Interrupt::IsEnabled()); assert(this == CurrentThread()); struct stack_frame stack_frame; struct stack_frame* user_stack_frame; #if defined(__i386__) user_stack_frame = (struct stack_frame*) (intctx->esp - offsetof(struct stack_frame, sigreturn) - 4); bool wrong_ip = intctx->eip != (uintptr_t) process->sigreturn + 2; #elif defined(__x86_64__) user_stack_frame = (struct stack_frame*) (intctx->rsp - offsetof(struct stack_frame, sigreturn) - 8); bool wrong_ip = intctx->rip != (uintptr_t) process->sigreturn + 2; #else #error "You need to locate the stack we passed the signal handler" #endif // Protect against sigreturn oriented programming (SROP) as described in // "Framing Signals - A Return to Portable Shellcode" (Bosman and Bos 2014). // If the we're not called from the kernel sigreturn page, it is not a valid // sigreturn. if ( wrong_ip ) { process->ExitThroughSignal(SIGABRT); Log::PrintF("%s[%ji]: sigreturn smashing detected: Bypassed kernel sigreturn page\n", process->program_image_path, (intmax_t) process->pid); // TODO: Allow debugging this event (see scram(2)). kthread_exit(); } // If no signals are being serviced by this thread at the moment, it is not // a valid sigreturn. if ( signal_count == 0 ) { process->ExitThroughSignal(SIGABRT); Log::PrintF("%s[%ji]: sigreturn smashing detected: Thread wasn't servicing a signal\n", process->program_image_path, (intmax_t) process->pid); // TODO: Allow debugging this event (see scram(2)). kthread_exit(); } // If a single signal is being serviced by this thread at the moment, the // stack pointer must be what we expect it to be. If there's multiple, we // don't know which one is the correct. (We could keep track of them and // ensure it's one of them, but that's not really worth it. The list of such // delivered signals could grow without bound because it's valid to longjmp // out of a signal handler) if ( signal_single && (uintptr_t) user_stack_frame != signal_single_frame ) { process->ExitThroughSignal(SIGABRT); Log::PrintF("%s[%ji]: sigreturn smashing detected: Stack pointer was wrong\n", process->program_image_path, (intmax_t) process->pid); // TODO: Allow debugging this event (see scram(2)). kthread_exit(); } // If we couldn't read the frame, the sigreturn is certainly bad. if ( !CopyFromUser(&stack_frame, user_stack_frame, sizeof(stack_frame)) ) { process->ExitThroughSignal(SIGABRT); Log::PrintF("%s[%ji]: sigreturn smashing detected: Couldn't read stack frame: %m\n", process->program_image_path, (intmax_t) process->pid); // TODO: Allow debugging this event (see scram(2)). kthread_exit(); } ZeroUser(user_stack_frame, sizeof(*user_stack_frame)); // If the random canary isn't correct, the sigreturn is certianly bad. if ( stack_frame.canary != (signal_canary ^ (uintptr_t) user_stack_frame) ) { process->ExitThroughSignal(SIGABRT); Log::PrintF("%s[%ji]: sigreturn smashing detected: Verification value was incorrect\n", process->program_image_path, (intmax_t) process->pid); // TODO: Allow debugging this event (see scram(2)). kthread_exit(); } ScopedLock lock(&process->signal_lock); memcpy(&signal_mask, &stack_frame.ucontext.uc_sigmask, sizeof(signal_mask)); memcpy(&signal_stack, &stack_frame.ucontext.uc_stack, sizeof(signal_stack)); signal_stack.ss_flags &= __SS_SUPPORTED_FLAGS; struct thread_registers resume_regs; Scheduler::SaveInterruptedContext(intctx, &resume_regs); DecodeMachineContext(&stack_frame.ucontext.uc_mcontext, &resume_regs); Scheduler::LoadInterruptedContext(intctx, &resume_regs); if ( signal_count != SIZE_MAX ) signal_count--; signal_single = false; UpdatePendingSignals(this); intctx->signal_pending = asm_signal_is_pending; lock.Reset(); assert(Interrupt::IsEnabled()); HandleSignal(intctx); } namespace Signal { void DispatchHandler(struct interrupt_context* intctx, void* /*user*/) { assert(Interrupt::IsEnabled()); return CurrentThread()->HandleSignal(intctx); } void ReturnHandler(struct interrupt_context* intctx, void* /*user*/) { assert(Interrupt::IsEnabled()); return CurrentThread()->HandleSigreturn(intctx); } void Init() { sigemptyset(&default_ignored_signals); sigaddset(&default_ignored_signals, SIGCHLD); sigaddset(&default_ignored_signals, SIGURG); sigaddset(&default_ignored_signals, SIGPWR); sigaddset(&default_ignored_signals, SIGWINCH); sigemptyset(&default_stop_signals); sigaddset(&default_stop_signals, SIGTSTP); sigaddset(&default_stop_signals, SIGTTIN); sigaddset(&default_stop_signals, SIGTTOU); sigemptyset(&unblockable_signals); sigaddset(&unblockable_signals, SIGKILL); sigaddset(&unblockable_signals, SIGSTOP); } } // namespace Signal } // namespace Sortix