From 15d6aae7017a1131a37036cff37e96dd8f145d88 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 19:46:57 +0200 Subject: [PATCH 01/14] kernel+libc: Implement basic signals --- apps/CMakeLists.txt | 1 + apps/signal-test.cpp | 20 +++++++ kernel/CMakeLists.txt | 1 + kernel/src/arch/x86_64/Thread.cpp | 93 +++++++++++++++++++++++++++++++ kernel/src/sys/Syscall.cpp | 4 ++ kernel/src/sys/exec.cpp | 12 ++++ kernel/src/sys/signal.cpp | 68 ++++++++++++++++++++++ kernel/src/thread/Scheduler.cpp | 6 ++ kernel/src/thread/Thread.cpp | 32 +++++++++++ kernel/src/thread/Thread.h | 15 +++++ libc/CMakeLists.txt | 2 + libc/include/bits/signal.h | 29 ++++++++++ libc/include/signal.h | 24 ++++++++ libc/src/arch/x86_64/sigreturn.S | 5 ++ libc/src/signal.cpp | 26 +++++++++ libluna/include/luna/Syscall.h | 2 +- 16 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 apps/signal-test.cpp create mode 100644 kernel/src/sys/signal.cpp create mode 100644 libc/include/bits/signal.h create mode 100644 libc/src/arch/x86_64/sigreturn.S create mode 100644 libc/src/signal.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index be45f1c8..d9d89610 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -30,6 +30,7 @@ luna_app(uname.cpp uname) luna_app(base64.cpp base64) luna_app(login.cpp login) luna_app(ipc-test.cpp ipc-test) +luna_app(signal-test.cpp signal-test) luna_app(mount.cpp mount) luna_app(umount.cpp umount) luna_app(ps.cpp ps) diff --git a/apps/signal-test.cpp b/apps/signal-test.cpp new file mode 100644 index 00000000..3d27491a --- /dev/null +++ b/apps/signal-test.cpp @@ -0,0 +1,20 @@ +#include +#include + +void handler(int) +{ + puts("I'm a signal handler"); +} + +int main() +{ + struct sigaction sa; + sa.sa_handler = handler; + sigaction(SIGABRT, &sa, NULL); + + raise(SIGABRT); + + puts("I'm outside the signal handler!"); + + return 0; +} diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cc3d5c82..10c3b425 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -40,6 +40,7 @@ set(SOURCES src/sys/uname.cpp src/sys/mount.cpp src/sys/resource.cpp + src/sys/signal.cpp src/fs/VFS.cpp src/fs/Pipe.cpp src/fs/Mount.cpp diff --git a/kernel/src/arch/x86_64/Thread.cpp b/kernel/src/arch/x86_64/Thread.cpp index 671e9eca..2934e68a 100644 --- a/kernel/src/arch/x86_64/Thread.cpp +++ b/kernel/src/arch/x86_64/Thread.cpp @@ -1,5 +1,9 @@ #include "thread/Thread.h" +#include "Log.h" +#include "memory/MemoryManager.h" +#include #include +#include bool is_in_kernel(Registers* regs) { @@ -31,6 +35,11 @@ void Thread::set_return(u64 ret) regs.rax = ret; } +u64 Thread::return_register() +{ + return regs.rax; +} + void Thread::init_regs_kernel() { memset(®s, 0, sizeof(Registers)); @@ -61,3 +70,87 @@ void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs) memcpy(regs, &new_thread->regs, sizeof(Registers)); } + +// FIXME: Move this function to a common location (also used in ThreadImage) +Result Thread::push_mem_on_stack(const u8* mem, usize size) +{ + if ((regs.rsp - size) < stack.bottom()) return err(E2BIG); + + if (!MemoryManager::validate_user_write((void*)(regs.rsp - size), size)) return err(EFAULT); + + regs.rsp -= size; + + memcpy((void*)regs.rsp, mem, size); + + return regs.rsp; +} + +Result Thread::pop_mem_from_stack(u8* mem, usize size) +{ + if ((regs.rsp + size) > stack.top()) return err(E2BIG); + + if (!MemoryManager::validate_user_read((void*)regs.rsp, size)) return err(EFAULT); + + memcpy(mem, (void*)regs.rsp, size); + + regs.rsp += size; + + return regs.rsp; +} + +bool Thread::deliver_signal(int signo, Registers* current_regs) +{ + auto handler = signal_handlers[signo - 1]; + check(handler.sa_handler != SIG_IGN); + check(handler.sa_handler != SIG_DFL); + + memcpy(®s, current_regs, sizeof(regs)); + + kinfoln("signal: delivering signal %d for thread %ld, ip=%p, sp=%p, handler=%p, sigreturn=%p", signo, id, + (void*)regs.rip, (void*)regs.rsp, (void*)handler.sa_handler, (void*)handler.__sa_sigreturn); + + regs.rsp -= 128; // Skip the red zone + + if (push_mem_on_stack((u8*)current_regs, sizeof(*current_regs)).has_error()) return false; + if (push_mem_on_stack((u8*)&signal_mask, sizeof(signal_mask)).has_error()) return false; + + u64 rsp = regs.rsp; + + regs.rsp = align_down<16>(regs.rsp); + if (push_mem_on_stack((u8*)&rsp, sizeof(u64)).has_error()) return false; + if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false; + + // FIXME: Do not add the current signal to the signal mask if SA_NODEFER is set. + signal_mask = handler.sa_mask | (1 << (signo - 1)); + + rsp = regs.rsp; + + init_regs_user(); + regs.rsp = rsp; + regs.rip = (u64)handler.sa_handler; + regs.rdi = signo; + + memcpy(current_regs, ®s, sizeof(regs)); + + return true; +} + +void Thread::sigreturn(Registers* current_regs) +{ + memcpy(®s, current_regs, sizeof(regs)); + + u64 rsp; + pop_mem_from_stack((u8*)&rsp, sizeof(rsp)); + regs.rsp = rsp; + pop_mem_from_stack((u8*)&signal_mask, sizeof(signal_mask)); + pop_mem_from_stack((u8*)current_regs, sizeof(*current_regs)); + memcpy(®s, current_regs, sizeof(regs)); + regs.cs = 0x18 | 3; + regs.ss = 0x20 | 3; + // FIXME: Using this, a program can craft a special RFLAGS that gives them a higher IOPL or other stuff. Find out + // exactly what bits to block from modifying. + + kinfoln("sigreturn: restored program state, sp=%p, ip=%p", (void*)regs.rsp, (void*)regs.rip); + + memcpy(current_regs, ®s, sizeof(regs)); +} diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index d45552aa..a19a35be 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -1,4 +1,5 @@ #include "sys/Syscall.h" +#include "thread/Scheduler.h" #include syscall_func_t syscalls[] = { @@ -13,6 +14,9 @@ i64 invoke_syscall(Registers* regs, SyscallArgs args, u64 syscall) if (syscall >= Syscalls::__count) { return -ENOSYS; } auto rc = syscalls[syscall](regs, args); + + Scheduler::current()->process_pending_signals(regs); + if (rc.has_error()) return -rc.error(); return (i64)rc.value(); } diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 5ef59522..fe6a8623 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -112,6 +112,11 @@ Result sys_execve(Registers* regs, SyscallArgs args) memcpy(regs, ¤t->regs, sizeof(*regs)); + for (int i = 0; i < NSIG; i++) + { + current->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 }; + } + kinfoln("exec: thread %lu was replaced with %s", current->id, path.chars()); return 0; @@ -151,6 +156,13 @@ Result sys_fork(Registers* regs, SyscallArgs) memcpy(&thread->regs, regs, sizeof(*regs)); + for (int i = 0; i < NSIG; i++) + { + auto sighandler = current->signal_handlers[i].sa_handler; + thread->signal_handlers[i] = { .sa_handler = sighandler, .sa_mask = 0, .sa_flags = 0 }; + } + thread->signal_mask = current->signal_mask; + thread->set_return(0); Scheduler::add_thread(thread); diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp new file mode 100644 index 00000000..a0bfdd36 --- /dev/null +++ b/kernel/src/sys/signal.cpp @@ -0,0 +1,68 @@ +#include "Log.h" +#include "memory/MemoryManager.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" + +Result sys_sigreturn(Registers* regs, SyscallArgs) +{ + auto* current = Scheduler::current(); + + current->sigreturn(regs); + + // Since we could be returning to anywhere in the program, we don't want to be changing the return value register + // (RAX on x86_64). But our syscall framework doesn't allow that, so we just set it to its current value. + return current->return_register(); +} + +Result sys_sigaction(Registers*, SyscallArgs args) +{ + auto* current = Scheduler::current(); + + int signo = (int)args[0]; + const struct sigaction* act = (const struct sigaction*)args[1]; + struct sigaction* oldact = (struct sigaction*)args[2]; + void* sigreturn = (void*)args[3]; + + if (signo > NSIG) return err(EINVAL); + if (signo <= 0) return err(EINVAL); + + if (oldact) + { + if (!MemoryManager::copy_to_user_typed(oldact, ¤t->signal_handlers[signo - 1])) return err(EFAULT); + } + + if (act) + { + struct sigaction kact; + if (!MemoryManager::copy_from_user_typed(act, &kact)) return err(EFAULT); + kact.__sa_sigreturn = sigreturn; + + kinfoln("sigaction: installing signal handler for signo=%d: handler=%p, sigreturn=%p", signo, + (void*)kact.sa_handler, sigreturn); + + current->signal_handlers[signo - 1] = kact; + } + + return 0; +} + +Result sys_kill(Registers*, SyscallArgs args) +{ + auto* current = Scheduler::current(); + + pid_t pid = (pid_t)args[0]; + int signo = (int)args[1]; + + // FIXME: Support this case. + if (pid <= 0) return err(ENOTSUP); + + auto* target = TRY(Result::from_option(Scheduler::find_by_pid(pid), ESRCH)); + if (current->auth.euid != 0 && current->auth.euid != target->auth.euid && current->auth.egid != target->auth.egid) + return err(EPERM); + if (target->is_kernel) return 0; + if (signo == 0) return 0; + + target->pending_signals |= 1 << (signo - 1); + + return 0; +} diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 77d94238..e033d1b2 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -155,6 +155,11 @@ namespace Scheduler image->apply(thread); thread->set_arguments(args.size(), argv, env.size(), envp); + for (int i = 0; i < NSIG; i++) + { + thread->signal_handlers[i] = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 }; + } + kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(), thread->sp(), thread->kernel_stack.top()); @@ -259,6 +264,7 @@ namespace Scheduler Thread* old_thread = g_current; Thread* new_thread = pick_task(); generic_switch_context(old_thread, new_thread, regs); + if (!is_in_kernel(regs)) new_thread->process_pending_signals(regs); } void invoke(Registers* regs) diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 8c7d1eb4..776d189d 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -1,4 +1,5 @@ #include "thread/Thread.h" +#include "Log.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #include @@ -97,6 +98,37 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa unreachable(); } +void Thread::process_pending_signals(Registers* current_regs) +{ + for (int i = 0; i < NSIG; i++) + { + if (signal_mask & (1 << i)) continue; + if (pending_signals & (1 << i)) + { + int signo = i + 1; + pending_signals &= ~(1 << i); + kinfoln("signal: executing signal %d for thread %ld", signo, id); + auto handler = signal_handlers[i]; + if (handler.sa_handler == SIG_IGN) + { + kinfoln("signal: ignoring signal (handler=SIG_IGN)"); + return; + } + if (handler.sa_handler == SIG_DFL) + { + default_signal: + kinfoln("signal: using default behavior (handler=SIG_DFL) (terminating)"); + // FIXME: Add different default handlers for different signals and add signal exit codes. + exit_and_signal_parent(255); + } + // If we fail to deliver the signal (usually because there's not enough space on the stack), execute the + // default action. + if (!deliver_signal(signo, current_regs)) goto default_signal; + return; + } + } +} + bool FileDescriptor::should_append() { return flags & O_APPEND; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index 6e979095..e568bb44 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -3,6 +3,7 @@ #include "arch/MMU.h" #include "fs/VFS.h" #include "memory/AddressSpace.h" +#include #include #include #include @@ -79,6 +80,11 @@ struct Thread : public LinkedListNode bool follow_last_symlink, SharedPtr* parent_inode = nullptr); + struct sigaction signal_handlers[NSIG]; + sigset_t signal_mask { 0 }; + + int pending_signals { 0 }; + FPData fp_data; ThreadState state = ThreadState::Runnable; @@ -128,6 +134,15 @@ struct Thread : public LinkedListNode u64 sp(); void set_return(u64 ret); + u64 return_register(); + + void process_pending_signals(Registers* current_regs); + + bool deliver_signal(int signo, Registers* current_regs); + void sigreturn(Registers* current_regs); + + Result push_mem_on_stack(const u8* mem, usize size); + Result pop_mem_from_stack(u8* mem, usize size); static void init(); }; diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 307e1523..5edda97a 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -21,6 +21,7 @@ set(SOURCES src/grp.cpp src/locale.cpp src/scanf.cpp + src/signal.cpp src/sys/stat.cpp src/sys/mman.cpp src/sys/wait.cpp @@ -36,6 +37,7 @@ if(${LUNA_ARCH} STREQUAL "x86_64") ${SOURCES} src/arch/x86_64/syscall.S src/arch/x86_64/setjmp.S + src/arch/x86_64/sigreturn.S ) endif() diff --git a/libc/include/bits/signal.h b/libc/include/bits/signal.h new file mode 100644 index 00000000..c562c5a8 --- /dev/null +++ b/libc/include/bits/signal.h @@ -0,0 +1,29 @@ +/* bits/signal.h: Signal-related definitions. */ + +#ifndef _BITS_SIGNAL_H +#define _BITS_SIGNAL_H + +typedef void (*__simple_sighandler_t)(int); + +#define SIG_IGN (__simple_sighandler_t)(-1) +#define SIG_DFL (__simple_sighandler_t)(-2) + +typedef int sigset_t; + +struct sigaction +{ + __simple_sighandler_t sa_handler; + sigset_t sa_mask; + int sa_flags; + void* __sa_sigreturn = nullptr; +}; + +enum __signals +{ + SIGABRT = 1, + __NSIG, +}; + +#define NSIG (__NSIG - 1) + +#endif diff --git a/libc/include/signal.h b/libc/include/signal.h index 0b0b6bf2..b12ccb80 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -3,6 +3,30 @@ #ifndef _SIGNAL_H #define _SIGNAL_H +#include +#include + typedef int sig_atomic_t; +#ifdef __cplusplus +extern "C" +{ +#endif + +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Wshadow" + /* Examine/change the current thread's signal disposition for a specific signal. */ + int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact); +#pragma GCC pop_options + + /* Send a signal to a specific process. */ + int kill(pid_t pid, int signo); + + /* Send a signal to the current thread. */ + int raise(int signo); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/libc/src/arch/x86_64/sigreturn.S b/libc/src/arch/x86_64/sigreturn.S new file mode 100644 index 00000000..44aeee30 --- /dev/null +++ b/libc/src/arch/x86_64/sigreturn.S @@ -0,0 +1,5 @@ +.global sigreturn +sigreturn: + mov $47, %rax + int $66 + ud2 diff --git a/libc/src/signal.cpp b/libc/src/signal.cpp new file mode 100644 index 00000000..07455bdd --- /dev/null +++ b/libc/src/signal.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +extern "C" void sigreturn(); + +extern "C" +{ + int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact) + { + long rc = syscall(SYS_sigaction, signo, act, oldact, sigreturn); + __errno_return(rc, int); + } + + int kill(pid_t pid, int signo) + { + long rc = syscall(SYS_kill, pid, signo); + __errno_return(rc, int); + } + + int raise(int signo) + { + return kill(getpid(), signo); + } +} diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 5a6d6609..93db68ab 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -6,7 +6,7 @@ _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(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ - _e(pivot_root) + _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) enum Syscalls { -- 2.34.1 From bdcb690a7a58122ec2fa6e76cf906e90043df5ef Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 19:48:46 +0200 Subject: [PATCH 02/14] kernel: Avoid processing unregistered signals for init --- kernel/src/thread/Thread.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 776d189d..1ad744a2 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -117,6 +117,12 @@ void Thread::process_pending_signals(Registers* current_regs) if (handler.sa_handler == SIG_DFL) { default_signal: + if (id == 1) + { + kwarnln("signal: init got a signal it has no handler for, ignoring"); + return; + } + kinfoln("signal: using default behavior (handler=SIG_DFL) (terminating)"); // FIXME: Add different default handlers for different signals and add signal exit codes. exit_and_signal_parent(255); -- 2.34.1 From fc3fdc2b8733caf9ff098b24368ced35f3906d89 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 19:59:01 +0200 Subject: [PATCH 03/14] kernel: Add default actions for signals --- kernel/src/thread/Thread.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 1ad744a2..1e17f89c 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -98,6 +98,16 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa unreachable(); } +enum class DefaultSignalAction +{ + Ignore, + Terminate, +}; + +static constexpr DefaultSignalAction default_actions[] = { + DefaultSignalAction::Terminate // SIGABRT +}; + void Thread::process_pending_signals(Registers* current_regs) { for (int i = 0; i < NSIG; i++) @@ -123,9 +133,15 @@ void Thread::process_pending_signals(Registers* current_regs) return; } - kinfoln("signal: using default behavior (handler=SIG_DFL) (terminating)"); - // FIXME: Add different default handlers for different signals and add signal exit codes. - exit_and_signal_parent(255); + kinfoln("signal: using default behavior (handler=SIG_DFL)"); + + auto action = default_actions[i]; + switch (action) + { + case DefaultSignalAction::Ignore: return; + // FIXME: Add signal exit codes. + case DefaultSignalAction::Terminate: exit_and_signal_parent(255); + } } // If we fail to deliver the signal (usually because there's not enough space on the stack), execute the // default action. -- 2.34.1 From cde467ee465cd821a71f068bfd89a6f6100d0175 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 20:16:06 +0200 Subject: [PATCH 04/14] kernel: Support returning termination signals from waitpid --- apps/init.cpp | 9 ++++++++- kernel/src/sys/signal.cpp | 2 +- kernel/src/thread/Thread.cpp | 12 ++++++++++-- kernel/src/thread/Thread.h | 6 ++++-- libc/include/bits/waitpid.h | 2 ++ libc/include/sys/wait.h | 4 +++- 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/apps/init.cpp b/apps/init.cpp index 4f5d268a..131c2166 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -328,7 +328,14 @@ int main() { if (service.pid.has_value() && service.pid.value() == child) { - do_log("[init] service %s exited with status %d\n", service.name.chars(), WEXITSTATUS(status)); + if (WIFEXITED(status)) + { + do_log("[init] service %s exited with status %d\n", service.name.chars(), WEXITSTATUS(status)); + } + else + { + do_log("[init] service %s was terminated by signal %d\n", service.name.chars(), WTERMSIG(status)); + } if (service.restart) { diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp index a0bfdd36..864fd1f8 100644 --- a/kernel/src/sys/signal.cpp +++ b/kernel/src/sys/signal.cpp @@ -62,7 +62,7 @@ Result sys_kill(Registers*, SyscallArgs args) if (target->is_kernel) return 0; if (signo == 0) return 0; - target->pending_signals |= 1 << (signo - 1); + target->send_signal(signo); return 0; } diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 1e17f89c..6031fc55 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -4,6 +4,7 @@ #include "thread/Scheduler.h" #include #include +#include #include #include #include @@ -70,7 +71,7 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink); } -[[noreturn]] void Thread::exit_and_signal_parent(u8 _status) +[[noreturn]] void Thread::exit_and_signal_parent(int _status) { if (this->id == 1) fail("the init process exited"); if (is_kernel) state = ThreadState::Dying; @@ -140,7 +141,8 @@ void Thread::process_pending_signals(Registers* current_regs) { case DefaultSignalAction::Ignore: return; // FIXME: Add signal exit codes. - case DefaultSignalAction::Terminate: exit_and_signal_parent(255); + case DefaultSignalAction::Terminate: exit_and_signal_parent(signo | _SIGBIT); + default: return; } } // If we fail to deliver the signal (usually because there's not enough space on the stack), execute the @@ -151,6 +153,12 @@ void Thread::process_pending_signals(Registers* current_regs) } } +void Thread::send_signal(int signo) +{ + check(signo > 0 && signo <= NSIG); + pending_signals |= 1 << (signo - 1); +} + bool FileDescriptor::should_append() { return flags & O_APPEND; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index e568bb44..be90fab1 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -91,7 +91,7 @@ struct Thread : public LinkedListNode bool is_kernel { true }; - u8 status { 0 }; + int status { 0 }; mode_t umask { 0 }; @@ -110,7 +110,7 @@ struct Thread : public LinkedListNode PageDirectory* active_directory { nullptr }; - [[noreturn]] void exit_and_signal_parent(u8 status); + [[noreturn]] void exit_and_signal_parent(int status); bool is_idle() { @@ -144,6 +144,8 @@ struct Thread : public LinkedListNode Result push_mem_on_stack(const u8* mem, usize size); Result pop_mem_from_stack(u8* mem, usize size); + void send_signal(int signo); + static void init(); }; diff --git a/libc/include/bits/waitpid.h b/libc/include/bits/waitpid.h index 8b7922b4..2ed3b15e 100644 --- a/libc/include/bits/waitpid.h +++ b/libc/include/bits/waitpid.h @@ -3,6 +3,8 @@ #ifndef _BITS_WAITPID_H #define _BITS_WAITPID_H +#define _SIGBIT 0x100 + #define WNOHANG 1 #endif diff --git a/libc/include/sys/wait.h b/libc/include/sys/wait.h index 4fa9c3e5..a10d9bf1 100644 --- a/libc/include/sys/wait.h +++ b/libc/include/sys/wait.h @@ -6,8 +6,10 @@ #include #include -#define WIFEXITED(ret) ((ret) | 0) +#define WIFEXITED(ret) (((ret)&_SIGBIT) == 0) #define WEXITSTATUS(ret) ((ret)&0xff) +#define WIFSIGNALED(ret) (((ret)&_SIGBIT) == _SIGBIT) +#define WTERMSIG(ret) ((ret)&0xff) #ifdef __cplusplus extern "C" -- 2.34.1 From 60d68b74e1677510de0fb565750492960dedbe3a Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 20:30:37 +0200 Subject: [PATCH 05/14] kernel: Define a good set of default signals Most of these have POSIX-defined numbers. --- kernel/src/thread/Thread.cpp | 25 ++++++++++++++++++++----- libc/include/bits/signal.h | 18 +++++++++++++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 6031fc55..d9b239a5 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -4,6 +4,7 @@ #include "thread/Scheduler.h" #include #include +#include #include #include #include @@ -106,26 +107,40 @@ enum class DefaultSignalAction }; 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) { 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)) { - int signo = i + 1; pending_signals &= ~(1 << i); kinfoln("signal: executing signal %d for thread %ld", signo, id); 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)"); return; } - if (handler.sa_handler == SIG_DFL) + if (handler.sa_handler == SIG_DFL || signo == SIGKILL || signo == SIGSTOP) { default_signal: if (id == 1) diff --git a/libc/include/bits/signal.h b/libc/include/bits/signal.h index c562c5a8..9cc47724 100644 --- a/libc/include/bits/signal.h +++ b/libc/include/bits/signal.h @@ -18,9 +18,25 @@ struct sigaction void* __sa_sigreturn = nullptr; }; +// The signals with explicit numbers have portable signal numbers. 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, }; -- 2.34.1 From 015419b8f54cd69554a795471150b8b93c5c981e Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 20:49:22 +0200 Subject: [PATCH 06/14] kernel: Generate signals when children exit / when faults occur Userspace can now catch segfaults! --- apps/signal-test.cpp | 15 ++++++--- kernel/src/arch/x86_64/CPU.cpp | 56 ++++++++++++++++++---------------- kernel/src/thread/Thread.cpp | 14 ++++++--- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/apps/signal-test.cpp b/apps/signal-test.cpp index 3d27491a..9062652c 100644 --- a/apps/signal-test.cpp +++ b/apps/signal-test.cpp @@ -1,20 +1,25 @@ #include #include +#include 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() { struct sigaction sa; sa.sa_handler = handler; - sigaction(SIGABRT, &sa, NULL); + sigaction(SIGSEGV, &sa, NULL); - raise(SIGABRT); - - puts("I'm outside the signal handler!"); +#pragma GCC diagnostic ignored "-Wnonnull" + char* str = nullptr; + memset(str, 0, 2); return 0; } diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 1ac1d957..3b5739c8 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -9,6 +9,7 @@ #include "sys/Syscall.h" #include "thread/Scheduler.h" #include "video/TextConsole.h" +#include #include #include #include @@ -70,64 +71,65 @@ void decode_page_fault_error_code(u64 code) (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); - u64 cr2; - asm volatile("mov %%cr2, %0" : "=r"(cr2)); - kerrorln("Page fault at RIP %lx while accessing %lx!", regs->rip, cr2); - - decode_page_fault_error_code(regs->error); + 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)) { - // 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); + Scheduler::current()->send_signal(signo); + Scheduler::current()->process_pending_signals(regs); + return; } CPU::efficient_halt(); } -[[noreturn]] void handle_general_protection_fault(Registers* regs) +void handle_page_fault(Registers* regs) { - CPU::disable_interrupts(); + u64 cr2; + asm volatile("mov %%cr2, %0" : "=r"(cr2)); + kerrorln("Page fault while accessing %lx!", cr2); + decode_page_fault_error_code(regs->error); + + handle_cpu_exception(SIGSEGV, nullptr, regs); +} + +void handle_general_protection_fault(Registers* regs) +{ kerrorln("General protection fault at RIP %lx, error code %lx!", regs->rip, regs->error); - CPU::print_stack_trace_at(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(); + handle_cpu_exception(SIGSEGV, nullptr, regs); } extern "C" void handle_x86_exception(Registers* regs) { + CPU::disable_interrupts(); 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 2: FIXME_UNHANDLED_INTERRUPT("NMI (Non-maskable interrupt)"); case 3: FIXME_UNHANDLED_INTERRUPT("Breakpoint"); case 4: FIXME_UNHANDLED_INTERRUPT("Overflow"); 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 10: FIXME_UNHANDLED_INTERRUPT("Invalid TSS"); case 11: FIXME_UNHANDLED_INTERRUPT("Segment not present"); case 12: FIXME_UNHANDLED_INTERRUPT("Stack-segment fault"); - case 13: handle_general_protection_fault(regs); - case 14: handle_page_fault(regs); - case 16: FIXME_UNHANDLED_INTERRUPT("x87 floating-point exception"); + case 13: handle_general_protection_fault(regs); return; + case 14: handle_page_fault(regs); return; + case 16: handle_cpu_exception(SIGFPE, "x87 floating-point exception", regs); return; case 17: FIXME_UNHANDLED_INTERRUPT("Alignment check"); case 19: FIXME_UNHANDLED_INTERRUPT("SIMD floating-point exception"); case 20: FIXME_UNHANDLED_INTERRUPT("Virtualization exception"); diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index d9b239a5..aa235d46 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -83,14 +83,18 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa return true; }); - if (parent && parent->state == ThreadState::Waiting) + if (parent) { - auto child = *parent->child_being_waited_for; - if (child == -1 || child == (pid_t)id) + if (parent->state == ThreadState::Waiting) { - parent->child_being_waited_for = (pid_t)id; - parent->wake_up(); + auto child = *parent->child_being_waited_for; + if (child == -1 || child == (pid_t)id) + { + parent->child_being_waited_for = (pid_t)id; + parent->wake_up(); + } } + else { parent->send_signal(SIGCHLD); } } state = ThreadState::Exited; -- 2.34.1 From 8066e8f1d82503834847b25543e556e774396135 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:01:59 +0200 Subject: [PATCH 07/14] kernel+libc: Implement sigprocmask() and friends --- kernel/src/sys/signal.cpp | 31 ++++++++++++++++++++ libc/include/bits/signal.h | 5 ++++ libc/include/signal.h | 18 ++++++++++++ libc/src/signal.cpp | 53 ++++++++++++++++++++++++++++++++++ libluna/include/luna/Syscall.h | 2 +- 5 files changed, 108 insertions(+), 1 deletion(-) diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp index 864fd1f8..e81952a0 100644 --- a/kernel/src/sys/signal.cpp +++ b/kernel/src/sys/signal.cpp @@ -2,6 +2,7 @@ #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" +#include Result sys_sigreturn(Registers* regs, SyscallArgs) { @@ -66,3 +67,33 @@ Result sys_kill(Registers*, SyscallArgs args) return 0; } + +Result 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, ¤t->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; +} diff --git a/libc/include/bits/signal.h b/libc/include/bits/signal.h index 9cc47724..2cf92f99 100644 --- a/libc/include/bits/signal.h +++ b/libc/include/bits/signal.h @@ -18,6 +18,11 @@ struct sigaction 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 { diff --git a/libc/include/signal.h b/libc/include/signal.h index b12ccb80..3cfa36cf 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -25,6 +25,24 @@ extern "C" /* Send a signal to the current thread. */ 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 } #endif diff --git a/libc/src/signal.cpp b/libc/src/signal.cpp index 07455bdd..9b55e78b 100644 --- a/libc/src/signal.cpp +++ b/libc/src/signal.cpp @@ -23,4 +23,57 @@ extern "C" { 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; + } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 93db68ab..c89beb66 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -6,7 +6,7 @@ _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(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 { -- 2.34.1 From 3df40beaf260d20fa6236dd209c77fb22ef4e7e4 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:08:23 +0200 Subject: [PATCH 08/14] libc: Rewrite abort() using the new signals --- libc/src/stdlib.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/libc/src/stdlib.cpp b/libc/src/stdlib.cpp index 5a31db25..c98ff791 100644 --- a/libc/src/stdlib.cpp +++ b/libc/src/stdlib.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -99,7 +100,25 @@ extern "C" __noreturn void abort() { - syscall(SYS_exit, 255); + // First, try to unblock SIGABRT and then raise it. + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGABRT); + sigprocmask(SIG_UNBLOCK, &set, nullptr); + + raise(SIGABRT); + + // Still here? The program must have catched it. Reset the disposition to default. + struct sigaction act; + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGABRT, &act, nullptr); + + raise(SIGABRT); + + // There is no way we could end up here, unless there is some sort of race condition or the kernel decided to + // change the default action for SIGABRT because it's a Tuesday. __builtin_unreachable(); } -- 2.34.1 From fe9827bbeb609ae4a27856b552db9216543ec3e4 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:09:12 +0200 Subject: [PATCH 09/14] kernel: Fix fallthrough in switch statement --- kernel/src/sys/signal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp index e81952a0..2bde914b 100644 --- a/kernel/src/sys/signal.cpp +++ b/kernel/src/sys/signal.cpp @@ -88,9 +88,9 @@ Result sys_sigprocmask(Registers*, SyscallArgs args) switch (how) { - case SIG_BLOCK: current->signal_mask |= kset; - case SIG_UNBLOCK: current->signal_mask &= ~kset; - case SIG_SETMASK: current->signal_mask = kset; + case SIG_BLOCK: current->signal_mask |= kset; break; + case SIG_UNBLOCK: current->signal_mask &= ~kset; break; + case SIG_SETMASK: current->signal_mask = kset; break; default: return err(EINVAL); } } -- 2.34.1 From 4a5947e10ec17586ddc06ce54c3e4c98a4ca300a Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:17:25 +0200 Subject: [PATCH 10/14] libc: Implement signal() --- libc/include/signal.h | 5 +++++ libc/src/signal.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/libc/include/signal.h b/libc/include/signal.h index 3cfa36cf..814ede80 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -8,6 +8,8 @@ typedef int sig_atomic_t; +#define SIG_ERR (__simple_sighandler_t)(-3) + #ifdef __cplusplus extern "C" { @@ -19,6 +21,9 @@ extern "C" int sigaction(int signo, const struct sigaction* act, struct sigaction* oldact); #pragma GCC pop_options + /* Change the current thread's signal disposition for a specific signal. */ + __simple_sighandler_t signal(int signo, __simple_sighandler_t handler); + /* Send a signal to a specific process. */ int kill(pid_t pid, int signo); diff --git a/libc/src/signal.cpp b/libc/src/signal.cpp index 9b55e78b..64849364 100644 --- a/libc/src/signal.cpp +++ b/libc/src/signal.cpp @@ -13,6 +13,18 @@ extern "C" __errno_return(rc, int); } + __simple_sighandler_t signal(int signo, __simple_sighandler_t handler) + { + struct sigaction act, oldact; + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + + if (sigaction(signo, &act, &oldact) < 0) return SIG_ERR; + + return oldact.sa_handler; + } + int kill(pid_t pid, int signo) { long rc = syscall(SYS_kill, pid, signo); -- 2.34.1 From 66365e15a70f49f2247a510e94cfce85ba647301 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:17:56 +0200 Subject: [PATCH 11/14] libc: Block and ignore appropriate signals in system() --- libc/src/stdlib.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libc/src/stdlib.cpp b/libc/src/stdlib.cpp index c98ff791..32150fc7 100644 --- a/libc/src/stdlib.cpp +++ b/libc/src/stdlib.cpp @@ -191,9 +191,6 @@ extern "C" return S_ISREG(st.st_mode); } - // FIXME: During the execution of system(), SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored, in - // the process that calls system(). - pid_t child = fork(); if (child == 0) { @@ -203,9 +200,21 @@ extern "C" if (child < 0) return -1; + sigset_t set, oldset; + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigprocmask(SIG_BLOCK, &set, &oldset); + + auto old_int = signal(SIGINT, SIG_IGN); + auto old_quit = signal(SIGQUIT, SIG_IGN); + int status; waitpid(child, &status, 0); + sigprocmask(SIG_SETMASK, &oldset, nullptr); + signal(SIGINT, old_int); + signal(SIGQUIT, old_quit); + return status; } -- 2.34.1 From e0b5acb2ab08397f2d6d9a5ca583549d71bc5b89 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:19:43 +0200 Subject: [PATCH 12/14] libc: Make struct sigaction C-compatible --- libc/include/bits/signal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libc/include/bits/signal.h b/libc/include/bits/signal.h index 2cf92f99..423787e1 100644 --- a/libc/include/bits/signal.h +++ b/libc/include/bits/signal.h @@ -15,7 +15,11 @@ struct sigaction __simple_sighandler_t sa_handler; sigset_t sa_mask; int sa_flags; +#ifdef __cplusplus void* __sa_sigreturn = nullptr; +#else + void* __sa_sigreturn; +#endif }; // Constants for the 'how' parameter in sigprocmask(). -- 2.34.1 From 237184a8bf52b2a4656f2031210b12d2b5d24ba5 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:39:22 +0200 Subject: [PATCH 13/14] libc+sh: Implement strsignal and use it in the shell --- apps/sh.cpp | 10 +++++++++- libc/include/string.h | 3 +++ libc/src/string.cpp | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/sh.cpp b/apps/sh.cpp index 7f247eb9..6bca57cf 100644 --- a/apps/sh.cpp +++ b/apps/sh.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -117,7 +118,14 @@ Result luna_main(int argc, char** argv) if (child == 0) { TRY(execute_command(cmd.view())); } - TRY(os::Process::wait(child, nullptr)); + int status; + TRY(os::Process::wait(child, &status)); + + if (WIFSIGNALED(status)) + { + int sig = WTERMSIG(status); + if (sig != SIGINT && sig != SIGQUIT) os::println("[sh] Process %d exited: %s", child, strsignal(sig)); + } } return 0; diff --git a/libc/include/string.h b/libc/include/string.h index 3349a828..aff7a35b 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -62,6 +62,9 @@ extern "C" /* Return the human-readable description of an error number. */ char* strerror(int errnum); + /* Return the human-readable description of a signal number. */ + char* strsignal(int signo); + /* Return the length of the initial segment of str which consists only of bytes in accept. */ size_t strspn(const char* str, const char* accept); diff --git a/libc/src/string.cpp b/libc/src/string.cpp index eabdc00a..510c465e 100644 --- a/libc/src/string.cpp +++ b/libc/src/string.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -12,4 +13,32 @@ extern "C" { return const_cast(error_string(errnum)); } + + static const char* __internal_strsignal(int signo) + { + switch (signo) + { + case SIGHUP: return "Hangup"; + case SIGINT: return "Terminal interrupt"; + case SIGQUIT: return "Terminal quit"; + case SIGILL: return "Invalid opcode"; + case SIGTRAP: return "Breakpoint trap"; + case SIGABRT: return "Aborted"; + case SIGCHLD: return "Child stopped or exited"; + case SIGFPE: return "Floating point exception"; + case SIGKILL: return "Killed"; + case SIGSTOP: return "Stopped"; + case SIGSEGV: return "Segmentation fault"; + case SIGCONT: return "Continued"; + case SIGPIPE: return "Broken pipe"; + case SIGALRM: return "Alarm signal"; + case SIGTERM: return "Terminated"; + default: return "Unknown signal"; + } + } + + char* strsignal(int signo) + { + return const_cast(__internal_strsignal(signo)); + } } -- 2.34.1 From 86d14e0d0eedaaabcdf6081962fb5294051dd858 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Jul 2023 21:54:04 +0200 Subject: [PATCH 14/14] kernel+libc: Add the SA_NODEFER and SA_RESETHAND flags for sigaction() --- apps/signal-test.cpp | 6 ++---- kernel/src/arch/x86_64/Thread.cpp | 12 ++++++++++-- libc/include/bits/signal.h | 4 ++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/signal-test.cpp b/apps/signal-test.cpp index 9062652c..b9f6df9a 100644 --- a/apps/signal-test.cpp +++ b/apps/signal-test.cpp @@ -5,16 +5,14 @@ void handler(int) { puts("I caught a segfault!"); - - struct sigaction sa; - sa.sa_handler = SIG_DFL; - sigaction(SIGSEGV, &sa, NULL); } int main() { struct sigaction sa; sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; sigaction(SIGSEGV, &sa, NULL); #pragma GCC diagnostic ignored "-Wnonnull" diff --git a/kernel/src/arch/x86_64/Thread.cpp b/kernel/src/arch/x86_64/Thread.cpp index 2934e68a..c17cd7d4 100644 --- a/kernel/src/arch/x86_64/Thread.cpp +++ b/kernel/src/arch/x86_64/Thread.cpp @@ -120,8 +120,8 @@ bool Thread::deliver_signal(int signo, Registers* current_regs) if (push_mem_on_stack((u8*)&rsp, sizeof(u64)).has_error()) return false; if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false; - // FIXME: Do not add the current signal to the signal mask if SA_NODEFER is set. - signal_mask = handler.sa_mask | (1 << (signo - 1)); + signal_mask = handler.sa_mask; + if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask |= (1 << (signo - 1)); rsp = regs.rsp; @@ -132,6 +132,14 @@ bool Thread::deliver_signal(int signo, Registers* current_regs) memcpy(current_regs, ®s, sizeof(regs)); + if (handler.sa_flags & SA_RESETHAND) + { + handler.sa_handler = SIG_DFL; + handler.sa_mask = 0; + handler.sa_flags = 0; + signal_handlers[signo - 1] = handler; + } + return true; } diff --git a/libc/include/bits/signal.h b/libc/include/bits/signal.h index 423787e1..792f9840 100644 --- a/libc/include/bits/signal.h +++ b/libc/include/bits/signal.h @@ -22,6 +22,10 @@ struct sigaction #endif }; +// Constants for sigaction's sa_flags field. +#define SA_NODEFER (1 << 0) +#define SA_RESETHAND (1 << 1) + // Constants for the 'how' parameter in sigprocmask(). #define SIG_BLOCK 0 #define SIG_UNBLOCK 1 -- 2.34.1