Compare commits

...

3 Commits

Author SHA1 Message Date
8066e8f1d8
kernel+libc: Implement sigprocmask() and friends
Some checks failed
continuous-integration/drone/pr Build is failing
2023-07-10 21:01:59 +02:00
015419b8f5
kernel: Generate signals when children exit / when faults occur
Userspace can now catch segfaults!
2023-07-10 20:49:22 +02:00
60d68b74e1
kernel: Define a good set of default signals
Most of these have POSIX-defined numbers.
2023-07-10 20:30:37 +02:00
8 changed files with 193 additions and 44 deletions

View File

@ -1,20 +1,25 @@
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
void handler(int) void handler(int)
{ {
puts("I'm a signal handler"); puts("I caught a segfault!");
struct sigaction sa;
sa.sa_handler = SIG_DFL;
sigaction(SIGSEGV, &sa, NULL);
} }
int main() int main()
{ {
struct sigaction sa; struct sigaction sa;
sa.sa_handler = handler; sa.sa_handler = handler;
sigaction(SIGABRT, &sa, NULL); sigaction(SIGSEGV, &sa, NULL);
raise(SIGABRT); #pragma GCC diagnostic ignored "-Wnonnull"
char* str = nullptr;
puts("I'm outside the signal handler!"); memset(str, 0, 2);
return 0; return 0;
} }

View File

@ -9,6 +9,7 @@
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include "video/TextConsole.h" #include "video/TextConsole.h"
#include <bits/signal.h>
#include <cpuid.h> #include <cpuid.h>
#include <luna/CString.h> #include <luna/CString.h>
#include <luna/CircularQueue.h> #include <luna/CircularQueue.h>
@ -70,64 +71,65 @@ void decode_page_fault_error_code(u64 code)
(code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : ""); (code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : "");
} }
[[noreturn]] void handle_page_fault(Registers* regs) void handle_cpu_exception(int signo, const char* err, Registers* regs)
{ {
CPU::disable_interrupts(); if (err) kerrorln("Caught CPU exception: %s", err);
kerrorln("RAX: %.16lx RBX: %.16lx RCX: %.16lx RDX: %.16lx", regs->rax, regs->rbx, regs->rcx, regs->rdx);
kerrorln("RBP: %.16lx RSP: %.16lx RDI: %.16lx RSI: %.16lx", regs->rbp, regs->rsp, regs->rdi, regs->rsi);
kerrorln("R8: %.16lx R9: %.16lx R10: %.16lx R11: %.16lx", regs->r8, regs->r9, regs->r10, regs->r11);
kerrorln("R12: %.16lx R13: %.16lx R14: %.16lx R15: %.16lx", regs->r12, regs->r13, regs->r14, regs->r15);
kerrorln("RIP: %.16lx CS: %.16lx SS: %.16lx FLAGS: %.16lx", regs->rip, regs->cs, regs->ss, regs->rflags);
CPU::print_stack_trace_at(regs);
if (!is_in_kernel(regs))
{
Scheduler::current()->send_signal(signo);
Scheduler::current()->process_pending_signals(regs);
return;
}
CPU::efficient_halt();
}
void handle_page_fault(Registers* regs)
{
u64 cr2; u64 cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2)); asm volatile("mov %%cr2, %0" : "=r"(cr2));
kerrorln("Page fault at RIP %lx while accessing %lx!", regs->rip, cr2); kerrorln("Page fault while accessing %lx!", cr2);
decode_page_fault_error_code(regs->error); decode_page_fault_error_code(regs->error);
CPU::print_stack_trace_at(regs); handle_cpu_exception(SIGSEGV, nullptr, regs);
if (!is_in_kernel(regs))
{
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
kerrorln("Current task %zu was terminated because of a page fault", Scheduler::current()->id);
Scheduler::current()->exit_and_signal_parent(127);
} }
CPU::efficient_halt(); void handle_general_protection_fault(Registers* regs)
}
[[noreturn]] void handle_general_protection_fault(Registers* regs)
{ {
CPU::disable_interrupts();
kerrorln("General protection fault at RIP %lx, error code %lx!", regs->rip, regs->error); kerrorln("General protection fault at RIP %lx, error code %lx!", regs->rip, regs->error);
CPU::print_stack_trace_at(regs); handle_cpu_exception(SIGSEGV, nullptr, regs);
if (!is_in_kernel(regs))
{
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
kerrorln("Current task %zu was terminated because of a general protection fault", Scheduler::current()->id);
Scheduler::current()->exit_and_signal_parent(127);
}
CPU::efficient_halt();
} }
extern "C" void handle_x86_exception(Registers* regs) extern "C" void handle_x86_exception(Registers* regs)
{ {
CPU::disable_interrupts();
switch (regs->isr) switch (regs->isr)
{ {
case 0: FIXME_UNHANDLED_INTERRUPT("Division by zero"); case 0: handle_cpu_exception(SIGFPE, "Division by zero", regs); return;
case 1: FIXME_UNHANDLED_INTERRUPT("Debug interrupt"); case 1: FIXME_UNHANDLED_INTERRUPT("Debug interrupt");
case 2: FIXME_UNHANDLED_INTERRUPT("NMI (Non-maskable interrupt)"); case 2: FIXME_UNHANDLED_INTERRUPT("NMI (Non-maskable interrupt)");
case 3: FIXME_UNHANDLED_INTERRUPT("Breakpoint"); case 3: FIXME_UNHANDLED_INTERRUPT("Breakpoint");
case 4: FIXME_UNHANDLED_INTERRUPT("Overflow"); case 4: FIXME_UNHANDLED_INTERRUPT("Overflow");
case 5: FIXME_UNHANDLED_INTERRUPT("Bound range exceeded"); case 5: FIXME_UNHANDLED_INTERRUPT("Bound range exceeded");
case 6: FIXME_UNHANDLED_INTERRUPT("Invalid opcode"); case 6: handle_cpu_exception(SIGILL, "Invalid opcode", regs); return;
case 7: FIXME_UNHANDLED_INTERRUPT("Device not available"); case 7: FIXME_UNHANDLED_INTERRUPT("Device not available");
case 10: FIXME_UNHANDLED_INTERRUPT("Invalid TSS"); case 10: FIXME_UNHANDLED_INTERRUPT("Invalid TSS");
case 11: FIXME_UNHANDLED_INTERRUPT("Segment not present"); case 11: FIXME_UNHANDLED_INTERRUPT("Segment not present");
case 12: FIXME_UNHANDLED_INTERRUPT("Stack-segment fault"); case 12: FIXME_UNHANDLED_INTERRUPT("Stack-segment fault");
case 13: handle_general_protection_fault(regs); case 13: handle_general_protection_fault(regs); return;
case 14: handle_page_fault(regs); case 14: handle_page_fault(regs); return;
case 16: FIXME_UNHANDLED_INTERRUPT("x87 floating-point exception"); case 16: handle_cpu_exception(SIGFPE, "x87 floating-point exception", regs); return;
case 17: FIXME_UNHANDLED_INTERRUPT("Alignment check"); case 17: FIXME_UNHANDLED_INTERRUPT("Alignment check");
case 19: FIXME_UNHANDLED_INTERRUPT("SIMD floating-point exception"); case 19: FIXME_UNHANDLED_INTERRUPT("SIMD floating-point exception");
case 20: FIXME_UNHANDLED_INTERRUPT("Virtualization exception"); case 20: FIXME_UNHANDLED_INTERRUPT("Virtualization exception");

View File

@ -2,6 +2,7 @@
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <bits/signal.h>
Result<u64> sys_sigreturn(Registers* regs, SyscallArgs) Result<u64> sys_sigreturn(Registers* regs, SyscallArgs)
{ {
@ -66,3 +67,33 @@ Result<u64> sys_kill(Registers*, SyscallArgs args)
return 0; return 0;
} }
Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
{
auto* current = Scheduler::current();
int how = (int)args[0];
const sigset_t* set = (const sigset_t*)args[1];
sigset_t* oldset = (sigset_t*)args[2];
if (oldset)
{
if (!MemoryManager::copy_to_user_typed(oldset, &current->signal_mask)) return err(EFAULT);
}
if (set)
{
sigset_t kset;
if (!MemoryManager::copy_from_user_typed(set, &kset)) return err(EFAULT);
switch (how)
{
case SIG_BLOCK: current->signal_mask |= kset;
case SIG_UNBLOCK: current->signal_mask &= ~kset;
case SIG_SETMASK: current->signal_mask = kset;
default: return err(EINVAL);
}
}
return 0;
}

View File

@ -4,6 +4,7 @@
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <bits/atfile.h> #include <bits/atfile.h>
#include <bits/open-flags.h> #include <bits/open-flags.h>
#include <bits/signal.h>
#include <bits/waitpid.h> #include <bits/waitpid.h>
#include <luna/Alloc.h> #include <luna/Alloc.h>
#include <luna/Atomic.h> #include <luna/Atomic.h>
@ -82,7 +83,9 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
return true; return true;
}); });
if (parent && parent->state == ThreadState::Waiting) if (parent)
{
if (parent->state == ThreadState::Waiting)
{ {
auto child = *parent->child_being_waited_for; auto child = *parent->child_being_waited_for;
if (child == -1 || child == (pid_t)id) if (child == -1 || child == (pid_t)id)
@ -91,6 +94,8 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
parent->wake_up(); parent->wake_up();
} }
} }
else { parent->send_signal(SIGCHLD); }
}
state = ThreadState::Exited; state = ThreadState::Exited;
} }
@ -106,26 +111,40 @@ enum class DefaultSignalAction
}; };
static constexpr DefaultSignalAction default_actions[] = { static constexpr DefaultSignalAction default_actions[] = {
DefaultSignalAction::Terminate // SIGABRT DefaultSignalAction::Terminate, // SIGHUP
DefaultSignalAction::Terminate, // SIGINT
DefaultSignalAction::Terminate, // SIGQUIT (dump core)
DefaultSignalAction::Terminate, // SIGILL (dump core)
DefaultSignalAction::Terminate, // SIGTRAP (dump core)
DefaultSignalAction::Terminate, // SIGABRT (dump core)
DefaultSignalAction::Ignore, // SIGCHLD
DefaultSignalAction::Terminate, // SIGFPE (dump core)
DefaultSignalAction::Terminate, // SIGKILL
DefaultSignalAction::Terminate, // SIGSTOP (FIXME: Support stopping and continuing)
DefaultSignalAction::Terminate, // SIGSEGV (dump core)
DefaultSignalAction::Ignore, // SIGCONT (FIXME: Support stopping and continuing)
DefaultSignalAction::Terminate, // SIGPIPE
DefaultSignalAction::Terminate, // SIGALRM
DefaultSignalAction::Terminate, // SIGTERM
}; };
void Thread::process_pending_signals(Registers* current_regs) void Thread::process_pending_signals(Registers* current_regs)
{ {
for (int i = 0; i < NSIG; i++) for (int i = 0; i < NSIG; i++)
{ {
if (signal_mask & (1 << i)) continue; int signo = i + 1;
if (signo != SIGKILL && signo != SIGSTOP && signal_mask & (1 << i)) continue;
if (pending_signals & (1 << i)) if (pending_signals & (1 << i))
{ {
int signo = i + 1;
pending_signals &= ~(1 << i); pending_signals &= ~(1 << i);
kinfoln("signal: executing signal %d for thread %ld", signo, id); kinfoln("signal: executing signal %d for thread %ld", signo, id);
auto handler = signal_handlers[i]; auto handler = signal_handlers[i];
if (handler.sa_handler == SIG_IGN) if (signo != SIGKILL && signo != SIGSTOP && handler.sa_handler == SIG_IGN)
{ {
kinfoln("signal: ignoring signal (handler=SIG_IGN)"); kinfoln("signal: ignoring signal (handler=SIG_IGN)");
return; return;
} }
if (handler.sa_handler == SIG_DFL) if (handler.sa_handler == SIG_DFL || signo == SIGKILL || signo == SIGSTOP)
{ {
default_signal: default_signal:
if (id == 1) if (id == 1)

View File

@ -18,9 +18,30 @@ struct sigaction
void* __sa_sigreturn = nullptr; void* __sa_sigreturn = nullptr;
}; };
// Constants for the 'how' parameter in sigprocmask().
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
// The signals with explicit numbers have portable signal numbers.
enum __signals enum __signals
{ {
SIGABRT = 1, SIGHUP = 1,
SIGINT = 2,
SIGQUIT = 3,
SIGILL = 4,
SIGTRAP = 5,
SIGABRT = 6,
SIGCHLD,
SIGFPE = 8,
SIGKILL = 9,
SIGSTOP,
SIGSEGV = 11,
SIGCONT,
SIGPIPE = 13,
SIGALRM = 14,
SIGTERM = 15,
// FIXME: Add the remaining signals.
__NSIG, __NSIG,
}; };

View File

@ -25,6 +25,24 @@ extern "C"
/* Send a signal to the current thread. */ /* Send a signal to the current thread. */
int raise(int signo); int raise(int signo);
/* Modify the current thread's signal mask. */
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
/* Clear all signals from set. */
int sigemptyset(sigset_t* set);
/* Add all signals to set. */
int sigfillset(sigset_t* set);
/* Add a specific signal to set. */
int sigaddset(sigset_t* set, int signo);
/* Remove a specific signal from set. */
int sigdelset(sigset_t* set, int signo);
/* Check if a signal is in set.*/
int sigismember(const sigset_t* set, int signo);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -23,4 +23,57 @@ extern "C"
{ {
return kill(getpid(), signo); return kill(getpid(), signo);
} }
int sigprocmask(int how, const sigset_t* set, sigset_t* oldset)
{
long rc = syscall(SYS_sigprocmask, how, set, oldset);
__errno_return(rc, int);
}
int sigemptyset(sigset_t* set)
{
*set = 0;
return 0;
}
int sigfillset(sigset_t* set)
{
*set = 0xffffffff;
return 0;
}
int sigaddset(sigset_t* set, int signo)
{
if (signo <= 0 || signo > NSIG)
{
errno = EINVAL;
return -1;
}
*set |= (1 << (signo - 1));
return 0;
}
int sigdelset(sigset_t* set, int signo)
{
if (signo <= 0 || signo > NSIG)
{
errno = EINVAL;
return -1;
}
*set &= ~(1 << (signo - 1));
return 0;
}
int sigismember(const sigset_t* set, int signo)
{
if (signo <= 0 || signo > NSIG)
{
errno = EINVAL;
return -1;
}
return (*set & (1 << (signo - 1))) > 0;
}
} }

View File

@ -6,7 +6,7 @@
_e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \ _e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask)
enum Syscalls enum Syscalls
{ {