From 1b63e9b4366f87019b36f1c0d9d33b70b4cfd50b Mon Sep 17 00:00:00 2001 From: Matheus Date: Fri, 17 May 2024 18:48:57 -0300 Subject: [PATCH] signals: more general changes --- kernel-src/arch/x86-64/context.asm | 1 + kernel-src/arch/x86-64/syscall.asm | 6 +- kernel-src/include/kernel/signal.h | 2 +- kernel-src/include/x86-64/arch/signal.h | 1 + kernel-src/sys/interrupt.c | 4 +- kernel-src/sys/jobctl.c | 34 ++++++++ kernel-src/sys/scheduler.c | 12 +-- kernel-src/sys/signal.c | 108 ++++++++++++++---------- kernel-src/sys/syscalls/execve.c | 3 + kernel-src/sys/syscalls/fork.c | 6 ++ kernel-src/sys/syscalls/kill.c | 3 +- kernel-src/sys/syscalls/poll.c | 3 +- kernel-src/sys/syscalls/waitpid.c | 6 ++ 13 files changed, 135 insertions(+), 54 deletions(-) diff --git a/kernel-src/arch/x86-64/context.asm b/kernel-src/arch/x86-64/context.asm index 8bfedf64..cdd392d9 100644 --- a/kernel-src/arch/x86-64/context.asm +++ b/kernel-src/arch/x86-64/context.asm @@ -1,6 +1,7 @@ section .text global arch_context_switch arch_context_switch: + xor rsi, rsi ; is syscall? (no) extern sched_userspacecheck call sched_userspacecheck ; compiled with no_caller_saved_registers diff --git a/kernel-src/arch/x86-64/syscall.asm b/kernel-src/arch/x86-64/syscall.asm index 2fe7909d..381a92cd 100644 --- a/kernel-src/arch/x86-64/syscall.asm +++ b/kernel-src/arch/x86-64/syscall.asm @@ -226,7 +226,11 @@ arch_syscall_entry: mov rdi, rsp add rdi, 8 ; context pointer is after r9 argument - extern sched_userspacecheck + xor rsi, rsi + inc rsi ; is syscall? (yes) + ; errno already in rdx + mov rcx, rax ; return value + extern sched_userspacecheck ; context, return, errno call sched_userspacecheck ; compiled with __attribute__((no_caller_saved_registers)) ; restore context diff --git a/kernel-src/include/kernel/signal.h b/kernel-src/include/kernel/signal.h index df0a18bb..7afc0c91 100644 --- a/kernel-src/include/kernel/signal.h +++ b/kernel-src/include/kernel/signal.h @@ -152,6 +152,6 @@ void signal_changemask(struct thread_t *thread, int how, sigset_t *new, sigset_t void signal_signalproc(struct proc_t *proc, int signal); void signal_signalthread(struct thread_t *thread, int signal, bool urgent); void signal_pending(struct thread_t *, sigset_t *sigset); -void signal_check(struct thread_t *thread, context_t *context); +void signal_check(struct thread_t *thread, context_t *context, bool syscall, uint64_t syscallret, uint64_t syscallerrno); #endif diff --git a/kernel-src/include/x86-64/arch/signal.h b/kernel-src/include/x86-64/arch/signal.h index 93a7e893..b395e486 100644 --- a/kernel-src/include/x86-64/arch/signal.h +++ b/kernel-src/include/x86-64/arch/signal.h @@ -5,6 +5,7 @@ #define ARCH_SIGNAL_STACK_GROWS_DOWNWARDS 1 #define ARCH_SIGNAL_REDZONE_SIZE 128 +#define ARCH_SIGNAL_SYSCALL_INSTRUCTION_SIZE 2 #define ARCH_SIGNAL_GETFROMRETURN(x) diff --git a/kernel-src/sys/interrupt.c b/kernel-src/sys/interrupt.c index 92a67604..acdcb70a 100644 --- a/kernel-src/sys/interrupt.c +++ b/kernel-src/sys/interrupt.c @@ -95,7 +95,7 @@ static void dopending(context_t *ctx) { } } -__attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t *context); +__attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t *context, bool syscall, uint64_t syscallret, uint64_t syscallerrno); void interrupt_isr(int vec, context_t *ctx) { isr_t *isr = &_cpu()->isr[vec]; @@ -116,7 +116,7 @@ void interrupt_isr(int vec, context_t *ctx) { isr->eoi(isr); if (ARCH_CONTEXT_ISUSER(ctx)) - sched_userspacecheck(ctx); + sched_userspacecheck(ctx, false, 0, 0); _cpu()->intstatus = ARCH_CONTEXT_INTSTATUS(ctx); } diff --git a/kernel-src/sys/jobctl.c b/kernel-src/sys/jobctl.c index 7776aa76..764cf621 100644 --- a/kernel-src/sys/jobctl.c +++ b/kernel-src/sys/jobctl.c @@ -288,5 +288,39 @@ void jobctl_procremove(proc_t *proc) { // proc can be null if the signal is being sent to the calling processes own process group // since that is only passed from a userland thread, we can assume _cpu()->thread->proc is not null int jobctl_signal(proc_t *proc, int signal) { + int error = 0; + bool intstatus = interrupt_set(false); + proc_t *leader; + proc_t *tmpproc = _cpu()->thread->proc; + if (proc == NULL) { + spinlock_acquire(&tmpproc->jobctllock); + leader = tmpproc->pgrp.leader ? tmpproc->pgrp.leader : tmpproc; + } else { + spinlock_acquire(&proc->jobctllock); + if (proc->pgrp.leader) { + error = ESRCH; + spinlock_release(&proc->jobctllock); + goto leave; + } + leader = proc; + spinlock_release(&proc->jobctllock); + } + + spinlock_acquire(&leader->pgrp.lock); + + proc_t *iterator = leader; + while (iterator) { + signal_signalproc(iterator, signal); + iterator = iterator->pgrp.nextmember; + } + + spinlock_release(&leader->pgrp.lock); + + leave: + if (proc == NULL) { + spinlock_release(&tmpproc->jobctllock); + } + interrupt_set(intstatus); + return error; } diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index 120fbbcd..7a56c7a9 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -290,11 +290,11 @@ void sched_procexit() { MUTEX_ACQUIRE(&sched_initproc->mutex, false); - bool haszombie = false; + int belowzombiecount = 0; while (lastchild && lastchild->sibling) { if (lastchild->state == SCHED_PROC_STATE_ZOMBIE) - haszombie = true; + ++belowzombiecount; lastchild->parent = sched_initproc; lastchild = lastchild->sibling; @@ -308,8 +308,10 @@ void sched_procexit() { MUTEX_RELEASE(&sched_initproc->mutex); MUTEX_RELEASE(&proc->mutex); + signal_signalproc(proc->parent, SIGCHLD); semaphore_signal(&proc->parent->waitsem); - if (haszombie) + // TODO sigaction flag for this + for (int i = 0; i < belowzombiecount; ++i) semaphore_signal(&sched_initproc->waitsem); } @@ -363,7 +365,7 @@ __attribute__((noreturn)) void sched_threadexit() { } // called right before going back to userspace in places like the syscall handler or arch_context_switch -__attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t *context) { +__attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t *context, bool syscall, uint64_t syscallerrno, uint64_t syscallret) { thread_t *thread = _cpu()->thread; if (thread == NULL || ARCH_CONTEXT_ISUSER(context) == false) return; @@ -373,7 +375,7 @@ __attribute__((no_caller_saved_registers)) void sched_userspacecheck(context_t * sched_threadexit(); } - signal_check(thread, context); + signal_check(thread, context, syscall, syscallret, syscallerrno); } void sched_stopotherthreads() { diff --git a/kernel-src/sys/signal.c b/kernel-src/sys/signal.c index 7b42e1bc..1604f184 100644 --- a/kernel-src/sys/signal.c +++ b/kernel-src/sys/signal.c @@ -2,6 +2,46 @@ #include #include +#define ACTION_TERM 0 +#define ACTION_IGN 1 +#define ACTION_CORE 2 +#define ACTION_STOP 3 +#define ACTION_CONT 4 + +static int defaultactions[NSIG] = { + [SIGABRT] = ACTION_CORE, + [SIGALRM] = ACTION_TERM, + [SIGBUS] = ACTION_CORE, + [SIGCHLD] = ACTION_IGN, + [SIGCONT] = ACTION_CONT, + [SIGFPE] = ACTION_CORE, + [SIGHUP] = ACTION_TERM, + [SIGILL] = ACTION_CORE, + [SIGINT] = ACTION_TERM, + [SIGIO] = ACTION_TERM, + [SIGKILL] = ACTION_TERM, + [SIGPIPE] = ACTION_TERM, + [SIGPOLL] = ACTION_TERM, + [SIGPROF] = ACTION_TERM, + [SIGPWR] = ACTION_TERM, + [SIGQUIT] = ACTION_CORE, + [SIGSEGV] = ACTION_CORE, + [SIGSTOP] = ACTION_STOP, + [SIGTSTP] = ACTION_STOP, + [SIGSYS] = ACTION_CORE, + [SIGTERM] = ACTION_TERM, + [SIGTRAP] = ACTION_CORE, + [SIGTTIN] = ACTION_STOP, + [SIGTTOU] = ACTION_STOP, + [SIGURG] = ACTION_IGN, + [SIGUSR1] = ACTION_TERM, + [SIGUSR2] = ACTION_TERM, + [SIGVTALRM] = ACTION_TERM, + [SIGXCPU] = ACTION_CORE, + [SIGXFSZ] = ACTION_CORE, + [SIGWINCH] = ACTION_IGN +}; + #define PROCESS_ENTER(proc) \ bool procintstatus = interrupt_set(false); \ spinlock_acquire(&proc->signals.lock); @@ -99,20 +139,30 @@ void signal_pending(struct thread_t *thread, sigset_t *sigset) { } void signal_signalthread(struct thread_t *thread, int signal, bool urgent) { + PROCESS_ENTER(thread->proc); THREAD_ENTER(thread); sigset_t *sigset = urgent ? &thread->signals.urgent : &thread->signals.pending; SIGNAL_SETON(sigset, signal); - if (signal == SIGKILL || signal == SIGSTOP || urgent || SIGNAL_GET(&thread->signals.mask, signal) == 0) + + void *address = thread->proc->signals.actions[signal].address; + bool notignored = address != SIG_IGN && ((address == SIG_DFL && defaultactions[signal] != ACTION_IGN) || address != SIG_DFL); + + if (signal == SIGKILL || signal == SIGSTOP || urgent || (SIGNAL_GET(&thread->signals.mask, signal) == 0 && notignored)) { sched_wakeup(thread, SCHED_WAKEUP_REASON_INTERRUPTED); + } THREAD_LEAVE(thread); + PROCESS_LEAVE(thread->proc); } void signal_signalproc(struct proc_t *proc, int signal) { // first, check if any threads have the signal unmasked, and set as pending for the first one MUTEX_ACQUIRE(&proc->mutex, false); PROCESS_ENTER(proc); + void *address = proc->signals.actions[signal].address; + bool notignored = address != SIG_IGN && ((address == SIG_DFL && defaultactions[signal] != ACTION_IGN) || address != SIG_DFL); + for (int i = 0; i < proc->threadtablesize; ++i) { if (proc->threads[i] == NULL || (proc->threads[i]->flags & SCHED_THREAD_FLAGS_DEAD)) continue; @@ -124,8 +174,10 @@ void signal_signalproc(struct proc_t *proc, int signal) { // TODO this } else if (SIGNAL_GET(&proc->threads[i]->signals.mask, signal) == 0 || signal == SIGKILL || signal == SIGSTOP) { SIGNAL_SETON(&proc->threads[i]->signals.pending, signal); - // TODO ipi if running isn't self - sched_wakeup(proc->threads[i], SCHED_WAKEUP_REASON_INTERRUPTED); + if (notignored) { + sched_wakeup(proc->threads[i], SCHED_WAKEUP_REASON_INTERRUPTED); + // TODO ipi if running isn't self + } THREAD_LEAVE(proc->threads[i]); PROCESS_LEAVE(proc); MUTEX_RELEASE(&proc->mutex); @@ -143,47 +195,8 @@ void signal_signalproc(struct proc_t *proc, int signal) { PROCESS_LEAVE(proc); } -#define ACTION_TERM 0 -#define ACTION_IGN 1 -#define ACTION_CORE 2 -#define ACTION_STOP 3 -#define ACTION_CONT 4 -static int defaultactions[NSIG] = { - [SIGABRT] = ACTION_CORE, - [SIGALRM] = ACTION_TERM, - [SIGBUS] = ACTION_CORE, - [SIGCHLD] = ACTION_IGN, - [SIGCONT] = ACTION_CONT, - [SIGFPE] = ACTION_CORE, - [SIGHUP] = ACTION_TERM, - [SIGILL] = ACTION_CORE, - [SIGINT] = ACTION_TERM, - [SIGIO] = ACTION_TERM, - [SIGKILL] = ACTION_TERM, - [SIGPIPE] = ACTION_TERM, - [SIGPOLL] = ACTION_TERM, - [SIGPROF] = ACTION_TERM, - [SIGPWR] = ACTION_TERM, - [SIGQUIT] = ACTION_CORE, - [SIGSEGV] = ACTION_CORE, - [SIGSTOP] = ACTION_STOP, - [SIGTSTP] = ACTION_STOP, - [SIGSYS] = ACTION_CORE, - [SIGTERM] = ACTION_TERM, - [SIGTRAP] = ACTION_CORE, - [SIGTTIN] = ACTION_STOP, - [SIGTTOU] = ACTION_STOP, - [SIGURG] = ACTION_IGN, - [SIGUSR1] = ACTION_TERM, - [SIGUSR2] = ACTION_TERM, - [SIGVTALRM] = ACTION_TERM, - [SIGXCPU] = ACTION_CORE, - [SIGXFSZ] = ACTION_CORE, - [SIGWINCH] = ACTION_IGN -}; - -void signal_check(struct thread_t *thread, context_t *context) { +void signal_check(struct thread_t *thread, context_t *context, bool syscall, uint64_t syscallret, uint64_t syscallerrno) { proc_t *proc = thread->proc; PROCESS_ENTER(proc); @@ -246,6 +259,8 @@ void signal_check(struct thread_t *thread, context_t *context) { case ACTION_CORE: case ACTION_TERM: proc->status = signal; + THREAD_LEAVE(thread); + PROCESS_LEAVE(proc); interrupt_set(true); sched_threadexit(); default: @@ -268,6 +283,13 @@ void signal_check(struct thread_t *thread, context_t *context) { #error unsupported #endif + // configure context for sigreturn() when a system call + if (syscall) { + CTX_ERRNO(context) = syscallerrno; + CTX_RET(context) = syscallret; + // TODO restartable system call stuff goes here + } + // configure stack frame sigframe_t *sigframe = stack; sigframe->restorer = action->restorer; diff --git a/kernel-src/sys/syscalls/execve.c b/kernel-src/sys/syscalls/execve.c index 7dc81d14..f53ea6b4 100644 --- a/kernel-src/sys/syscalls/execve.c +++ b/kernel-src/sys/syscalls/execve.c @@ -127,6 +127,9 @@ syscallret_t syscall_execve(context_t *context, char *upath, char *uargv[], char fd_close(fd); } + memset(&_cpu()->thread->signals.stack, 0, sizeof(stack_t)); + memset(&proc->signals.actions[0], 0, sizeof(sigaction_t) * NSIG); + error: free(path); if (argv) diff --git a/kernel-src/sys/syscalls/fork.c b/kernel-src/sys/syscalls/fork.c index 782caed0..b1906274 100644 --- a/kernel-src/sys/syscalls/fork.c +++ b/kernel-src/sys/syscalls/fork.c @@ -54,6 +54,12 @@ syscallret_t syscall_fork(context_t *ctx) { CTX_ERRNO(&nthread->context) = 0; jobctl_addproc(proc, nproc); + memcpy(&nthread->signals.mask, &_cpu()->thread->signals.mask, sizeof(sigset_t)); + memcpy(&nthread->signals.pending, &_cpu()->thread->signals.pending, sizeof(sigset_t)); + memcpy(&nthread->signals.stack, &_cpu()->thread->signals.stack, sizeof(stack_t)); + memcpy(&nproc->signals.pending, &proc->signals.pending, sizeof(sigset_t)); + memcpy(&nproc->signals.actions, &proc->signals.actions[0], sizeof(sigaction_t) * NSIG); + ret.ret = nproc->pid; sched_queue(nthread); diff --git a/kernel-src/sys/syscalls/kill.c b/kernel-src/sys/syscalls/kill.c index e6694645..d8e1a15f 100644 --- a/kernel-src/sys/syscalls/kill.c +++ b/kernel-src/sys/syscalls/kill.c @@ -1,6 +1,7 @@ #include #include #include +#include syscallret_t syscall_kill(context_t *context, int pid, int signal) { syscallret_t ret = { @@ -23,7 +24,7 @@ syscallret_t syscall_kill(context_t *context, int pid, int signal) { return ret; } - proc_t *target = sched_getprocfrompid(pid); + proc_t *target = sched_getprocfrompid(effectivepid); if (target == NULL && pid != 0) { ret.errno = ESRCH; return ret; diff --git a/kernel-src/sys/syscalls/poll.c b/kernel-src/sys/syscalls/poll.c index 3153cca0..e1d47459 100644 --- a/kernel-src/sys/syscalls/poll.c +++ b/kernel-src/sys/syscalls/poll.c @@ -37,7 +37,7 @@ syscallret_t syscall_poll(context_t *, pollfd_t *fds, size_t nfds, int timeoutms goto cleanup; } - polldesc_t desc; + polldesc_t desc = {0}; // when nfds is 0, poll is used as a sleep. // therefore, initialize the desc size to one descriptor in that case. ret.errno = poll_initdesc(&desc, nfds == 0 ? 1 : nfds); @@ -72,6 +72,7 @@ syscallret_t syscall_poll(context_t *, pollfd_t *fds, size_t nfds, int timeoutms if (eventcount == 0 && timeoutms != 0) { ret.errno = poll_dowait(&desc, (timeoutms == -1 ? 0 : timeoutms) * 1000); + if (ret.errno == 0 && desc.event) { int fd = ((uintptr_t)desc.event - (uintptr_t)desc.data) / sizeof(polldata_t); fdsbuff[fd].revents = desc.data[fd].revents; diff --git a/kernel-src/sys/syscalls/waitpid.c b/kernel-src/sys/syscalls/waitpid.c index 4d8672eb..2ad04962 100644 --- a/kernel-src/sys/syscalls/waitpid.c +++ b/kernel-src/sys/syscalls/waitpid.c @@ -25,6 +25,12 @@ syscallret_t syscall_waitpid(context_t *context, pid_t pid, int *status, int opt proc_t *iterator = proc->child; proc_t *desired = NULL; + if (iterator == NULL) { + ret.errno = ECHILD; + MUTEX_RELEASE(&proc->mutex); + return ret; + } + for (;;) { if (iterator == NULL) { if (pid > 0 && desired == NULL) {