Kernel: Userspace signal handlers are now called one at a time

I added a syscall for telling the kernel when signal execution has
finished. We should send a random hash or id to the signal trampoline
that we would include in the syscall, so validity of signal exit can
be confirmed.
This commit is contained in:
Bananymous 2023-07-23 13:09:04 +03:00
parent 7391d91317
commit adb14ba373
9 changed files with 77 additions and 6 deletions

View File

@ -46,6 +46,7 @@ set(KERNEL_SOURCES
kernel/Scheduler.cpp
kernel/Semaphore.cpp
kernel/Serial.cpp
kernel/Signal.cpp
kernel/SpinLock.cpp
kernel/SSP.cpp
kernel/Storage/ATABus.cpp

View File

@ -24,6 +24,9 @@ signal_trampoline:
movq 120(%rsp), %rax
call *%rax
movq 128(%rsp), %rdi
call signal_done
popq %r15
popq %r14
popq %r13

View File

@ -38,6 +38,7 @@ namespace Kernel
void setup_exec();
bool has_signal_to_execute() const;
void set_signal_done(int signal);
void handle_next_signal();
void handle_signal(int signal, uintptr_t& return_rsp, uintptr_t& return_rip);

8
kernel/kernel/Signal.cpp Normal file
View File

@ -0,0 +1,8 @@
#include <sys/syscall.h>
#include <kernel/Syscall.h>
extern "C" __attribute__((section(".userspace")))
void signal_done(int signal)
{
Kernel::syscall(SYS_SIGNAL_DONE, signal);
}

View File

@ -22,6 +22,12 @@ namespace Kernel
{
Thread::current().set_in_syscall(true);
if (syscall == SYS_SIGNAL_DONE)
{
Thread::current().set_signal_done((int)arg1);
return 0;
}
asm volatile("sti");
(void)arg1;
@ -149,6 +155,9 @@ namespace Kernel
case SYS_SIGNAL:
ret = Process::current().sys_signal((int)arg1, (void (*)(int))arg2);
break;
case SYS_SIGNAL_DONE:
// Handled above
ASSERT_NOT_REACHED();
default:
dwarnln("Unknown syscall {}", syscall);
break;

View File

@ -150,6 +150,19 @@ namespace Kernel
return !m_signal_queue.empty() && !m_handling_signal;
}
void Thread::set_signal_done(int signal)
{
ASSERT(!interrupts_enabled());
if (!m_handling_signal)
derrorln("set_signal_done called while not handling singal");
else if (m_signal_queue.empty())
derrorln("set_signal_done called and there are no signals in queue");
else if (m_signal_queue.front() != signal)
derrorln("set_signal_done called with wrong signal");
else
m_signal_queue.pop();
}
void Thread::handle_next_signal()
{
ASSERT(!interrupts_enabled());
@ -167,8 +180,6 @@ namespace Kernel
vaddr_t signal_handler = process().m_signal_handlers[signal];
m_handling_signal = true;
// Skip masked and ignored signals
if (m_signal_mask & (1ull << signal))
;
@ -176,13 +187,15 @@ namespace Kernel
;
else if (signal_handler != (vaddr_t)SIG_DFL)
{
// call userspace signal handlers
// FIXME: signal trampoline should take a hash etc
// to only allow marking signals done from it
m_handling_signal = true;
write_to_stack(return_rsp, return_rip);
write_to_stack(return_rsp, signal);
write_to_stack(return_rsp, signal_handler);
return_rip = (uintptr_t)signal_trampoline;
// FIXME: we should only mark this signal as done when
// handler returns
return;
}
else
{
@ -241,7 +254,6 @@ namespace Kernel
}
m_signal_queue.pop();
m_handling_signal = false;
}
void Thread::validate_stack() const

View File

@ -44,6 +44,7 @@ __BEGIN_DECLS
#define SYS_RAISE 37
#define SYS_KILL 38
#define SYS_SIGNAL 39
#define SYS_SIGNAL_DONE 40
__END_DECLS

View File

@ -286,6 +286,9 @@ long syscall(long syscall, ...)
ret = Kernel::syscall(SYS_SIGNAL, signal, (uintptr_t)handler);
break;
}
case SYS_SIGNAL_DONE:
// Should not be called by an user
// fall through
default:
puts("LibC: Unhandeled syscall");
ret = -ENOSYS;

View File

@ -281,6 +281,39 @@ int execute_command(BAN::Vector<BAN::String>& args)
return 1;
}
}
else if (args.front() == "signal-test"sv)
{
pid_t pid = fork();
if (pid == 0)
{
if (signal(SIGSEGV, [](int) { printf("SIGSEGV\n"); }) == SIG_ERR)
{
perror("signal");
exit(1);
}
printf("child\n");
for (;;);
}
if (pid == -1)
{
perror("fork");
return 1;
}
sleep(1);
if (kill(pid, SIGSEGV) == -1)
{
perror("kill");
return 1;
}
sleep(1);
if (kill(pid, SIGTERM) == -1)
{
perror("kill");
return 1;
}
}
else if (args.front() == "cd"sv)
{
if (args.size() > 2)