Kernel/LibC: Implement sigaltstack

This commit is contained in:
Bananymous 2025-08-20 20:17:49 +03:00
parent def236b7cd
commit 0dfe0b7023
10 changed files with 109 additions and 8 deletions

View File

@ -34,4 +34,6 @@ signal_trampoline:
addl $8, %esp addl $8, %esp
popf popf
movl (%esp), %esp
ret ret

View File

@ -59,5 +59,7 @@ signal_trampoline:
addq $16, %rsp addq $16, %rsp
popfq popfq
movq (%rsp), %rsp
// return over red-zone // return over red-zone
ret $128 ret $128

View File

@ -195,6 +195,7 @@ namespace Kernel
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset); BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
BAN::ErrorOr<long> sys_sigsuspend(const sigset_t* set); BAN::ErrorOr<long> sys_sigsuspend(const sigset_t* set);
BAN::ErrorOr<long> sys_sigwait(const sigset_t* set, int* sig); BAN::ErrorOr<long> sys_sigwait(const sigset_t* set, int* sig);
BAN::ErrorOr<long> sys_sigaltstack(const stack_t* ss, stack_t* oss);
BAN::ErrorOr<long> sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime); BAN::ErrorOr<long> sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime);
BAN::ErrorOr<long> sys_yield(); BAN::ErrorOr<long> sys_yield();

View File

@ -63,6 +63,8 @@ namespace Kernel
void add_signal(int signal); void add_signal(int signal);
void set_suspend_signal_mask(uint64_t sigmask); void set_suspend_signal_mask(uint64_t sigmask);
BAN::ErrorOr<void> sigaltstack(const stack_t* ss, stack_t* oss);
// blocks current thread and returns either on unblock, eintr, spuriously or after timeout // blocks current thread and returns either on unblock, eintr, spuriously or after timeout
// if mutex is not nullptr, it will be atomically freed before blocking and automatically locked on wake // if mutex is not nullptr, it will be atomically freed before blocking and automatically locked on wake
BAN::ErrorOr<void> sleep_or_eintr_ns(uint64_t ns); BAN::ErrorOr<void> sleep_or_eintr_ns(uint64_t ns);
@ -140,6 +142,8 @@ namespace Kernel
static void on_exit_trampoline(Thread*); static void on_exit_trampoline(Thread*);
void on_exit(); void on_exit();
bool currently_on_alternate_stack() const;
private: private:
// NOTE: this is the first member to force it being last destructed // NOTE: this is the first member to force it being last destructed
// {kernel,userspace}_stack has to be destroyed before page table // {kernel,userspace}_stack has to be destroyed before page table
@ -164,6 +168,7 @@ namespace Kernel
uint64_t m_signal_block_mask { 0 }; uint64_t m_signal_block_mask { 0 };
BAN::Optional<uint64_t> m_signal_suspend_mask; BAN::Optional<uint64_t> m_signal_suspend_mask;
SpinLock m_signal_lock; SpinLock m_signal_lock;
stack_t m_signal_alt_stack { nullptr, 0, SS_DISABLE };
static_assert(_SIGMAX < 64); static_assert(_SIGMAX < 64);
mutable SpinLock m_cpu_time_lock; mutable SpinLock m_cpu_time_lock;

View File

@ -2701,6 +2701,19 @@ namespace Kernel
} }
} }
BAN::ErrorOr<long> Process::sys_sigaltstack(const stack_t* ss, stack_t* oss)
{
LockGuard _(m_process_lock);
if (ss != nullptr)
TRY(validate_pointer_access(ss, sizeof(stack_t), false));
if (oss != nullptr)
TRY(validate_pointer_access(oss, sizeof(stack_t), true));
TRY(Thread::current().sigaltstack(ss, oss));
return 0;
}
BAN::ErrorOr<long> Process::sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime) BAN::ErrorOr<long> Process::sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime)
{ {
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(addr); const vaddr_t vaddr = reinterpret_cast<vaddr_t>(addr);

View File

@ -92,14 +92,14 @@ namespace Kernel
if (ret.is_error() && ret.error().is_kernel_error()) if (ret.is_error() && ret.error().is_kernel_error())
Kernel::panic("Kernel error while returning to userspace {}", ret.error()); Kernel::panic("Kernel error while returning to userspace {}", ret.error());
Processor::set_interrupt_state(InterruptState::Disabled);
auto& current_thread = Thread::current(); auto& current_thread = Thread::current();
if (current_thread.can_add_signal_to_execute()) if (current_thread.can_add_signal_to_execute())
if (current_thread.handle_signal()) if (current_thread.handle_signal())
if (ret.is_error() && ret.error().get_error_code() == EINTR && is_restartable_syscall(syscall)) if (ret.is_error() && ret.error().get_error_code() == EINTR && is_restartable_syscall(syscall))
ret = BAN::Error::from_errno(ERESTART); ret = BAN::Error::from_errno(ERESTART);
Processor::set_interrupt_state(InterruptState::Disabled);
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing); ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
if (ret.is_error()) if (ret.is_error())

View File

@ -507,6 +507,7 @@ namespace Kernel
ASSERT(is_userspace()); ASSERT(is_userspace());
auto state = m_signal_lock.lock(); auto state = m_signal_lock.lock();
ASSERT(state == InterruptState::Disabled);
auto& interrupt_stack = *reinterpret_cast<InterruptStack*>(kernel_stack_top() - sizeof(InterruptStack)); auto& interrupt_stack = *reinterpret_cast<InterruptStack*>(kernel_stack_top() - sizeof(InterruptStack));
ASSERT(GDT::is_user_segment(interrupt_stack.cs)); ASSERT(GDT::is_user_segment(interrupt_stack.cs));
@ -530,6 +531,7 @@ namespace Kernel
vaddr_t signal_handler; vaddr_t signal_handler;
bool has_sa_restart; bool has_sa_restart;
vaddr_t signal_stack_top = 0;
{ {
SpinLockGuard _(m_process->m_signal_lock); SpinLockGuard _(m_process->m_signal_lock);
@ -542,6 +544,10 @@ namespace Kernel
handler.sa_handler = SIG_DFL; handler.sa_handler = SIG_DFL;
has_sa_restart = !!(handler.sa_flags & SA_RESTART); has_sa_restart = !!(handler.sa_flags & SA_RESTART);
const auto& alt_stack = m_signal_alt_stack;
if (alt_stack.ss_flags != SS_DISABLE && (handler.sa_flags & SA_ONSTACK) && !currently_on_alternate_stack())
signal_stack_top = reinterpret_cast<vaddr_t>(alt_stack.ss_sp) + alt_stack.ss_size;
} }
m_signal_pending_mask &= ~(1ull << signal); m_signal_pending_mask &= ~(1ull << signal);
@ -553,6 +559,8 @@ namespace Kernel
m_signal_suspend_mask.clear(); m_signal_suspend_mask.clear();
} }
m_signal_lock.unlock(state);
if (signal_handler == (vaddr_t)SIG_IGN) if (signal_handler == (vaddr_t)SIG_IGN)
; ;
else if (signal_handler != (vaddr_t)SIG_DFL) else if (signal_handler != (vaddr_t)SIG_DFL)
@ -561,7 +569,32 @@ namespace Kernel
#if ARCH(x86_64) #if ARCH(x86_64)
interrupt_stack.sp -= 128; // skip possible red-zone interrupt_stack.sp -= 128; // skip possible red-zone
#endif #endif
{
// Make sure stack is allocated
const vaddr_t pages[3] {
(interrupt_stack.sp - sizeof(uintptr_t)) & PAGE_ADDR_MASK,
(signal_stack_top - 4 * sizeof(uintptr_t)) & PAGE_ADDR_MASK,
(signal_stack_top - 1 * sizeof(uintptr_t)) & PAGE_ADDR_MASK,
};
for (size_t i = 0; i < 3; i++)
{
if (m_process->page_table().get_page_flags(pages[i]) & PageTable::Flags::Present)
continue;
Processor::set_interrupt_state(InterruptState::Enabled);
if (auto ret = m_process->allocate_page_for_demand_paging(pages[i], true, false); ret.is_error() || !ret.value())
m_process->exit(128 + SIGSEGV, SIGSEGV);
Processor::set_interrupt_state(InterruptState::Disabled);
}
}
write_to_stack(interrupt_stack.sp, interrupt_stack.ip); write_to_stack(interrupt_stack.sp, interrupt_stack.ip);
const vaddr_t old_stack = interrupt_stack.sp;
if (signal_stack_top)
interrupt_stack.sp = signal_stack_top;
write_to_stack(interrupt_stack.sp, old_stack);
write_to_stack(interrupt_stack.sp, interrupt_stack.flags); write_to_stack(interrupt_stack.sp, interrupt_stack.flags);
write_to_stack(interrupt_stack.sp, signal); write_to_stack(interrupt_stack.sp, signal);
write_to_stack(interrupt_stack.sp, signal_handler); write_to_stack(interrupt_stack.sp, signal_handler);
@ -582,7 +615,6 @@ namespace Kernel
case SIGTRAP: case SIGTRAP:
case SIGXCPU: case SIGXCPU:
case SIGXFSZ: case SIGXFSZ:
m_signal_lock.unlock(state);
process().exit(128 + signal, signal | 0x80); process().exit(128 + signal, signal | 0x80);
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
@ -598,7 +630,6 @@ namespace Kernel
case SIGPOLL: case SIGPOLL:
case SIGPROF: case SIGPROF:
case SIGVTALRM: case SIGVTALRM:
m_signal_lock.unlock(state);
process().exit(128 + signal, signal); process().exit(128 + signal, signal);
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
@ -620,8 +651,6 @@ namespace Kernel
} }
} }
m_signal_lock.unlock(state);
return has_sa_restart; return has_sa_restart;
} }
@ -657,6 +686,46 @@ namespace Kernel
m_signal_block_mask = sigmask; m_signal_block_mask = sigmask;
} }
bool Thread::currently_on_alternate_stack() const
{
ASSERT(m_signal_lock.current_processor_has_lock());
if (m_signal_alt_stack.ss_flags == SS_ONSTACK)
return false;
const vaddr_t stack_bottom = reinterpret_cast<vaddr_t>(m_signal_alt_stack.ss_sp);
const vaddr_t stack_top = stack_bottom + m_signal_alt_stack.ss_size;
const vaddr_t sp = m_interrupt_stack.sp;
return stack_bottom <= sp && sp <= stack_top;
}
BAN::ErrorOr<void> Thread::sigaltstack(const stack_t* ss, stack_t* oss)
{
SpinLockGuard _(m_signal_lock);
const bool on_alt_stack = currently_on_alternate_stack();
if (oss)
{
*oss = m_signal_alt_stack;
if (on_alt_stack)
oss->ss_flags = SS_ONSTACK;
}
if (ss)
{
if (on_alt_stack)
return BAN::Error::from_errno(EPERM);
if (ss->ss_flags && ss->ss_flags != SS_DISABLE)
return BAN::Error::from_errno(EINVAL);
if (ss->ss_size < MINSIGSTKSZ)
return BAN::Error::from_errno(ENOMEM);
m_signal_alt_stack = *ss;
}
return {};
}
BAN::ErrorOr<void> Thread::sleep_or_eintr_ns(uint64_t ns) BAN::ErrorOr<void> Thread::sleep_or_eintr_ns(uint64_t ns)
{ {
if (is_interrupted_by_signal()) if (is_interrupted_by_signal())

View File

@ -132,8 +132,9 @@ struct sigevent
#define SA_SIGINFO 0x010 #define SA_SIGINFO 0x010
#define SA_NOCLDWAIT 0x020 #define SA_NOCLDWAIT 0x020
#define SA_NODEFER 0x040 #define SA_NODEFER 0x040
#define SS_ONSTACK 0x080
#define SS_DISABLE 0x100 #define SS_ONSTACK 1
#define SS_DISABLE 2
#define MINSIGSTKSZ 4096 #define MINSIGSTKSZ 4096
#define SIGSTKSZ 4096 #define SIGSTKSZ 4096

View File

@ -92,6 +92,7 @@ __BEGIN_DECLS
O(SYS_SIGPROCMASK, sigprocmask) \ O(SYS_SIGPROCMASK, sigprocmask) \
O(SYS_SIGSUSPEND, sigsuspend) \ O(SYS_SIGSUSPEND, sigsuspend) \
O(SYS_SIGWAIT, sigwait) \ O(SYS_SIGWAIT, sigwait) \
O(SYS_SIGALTSTACK, sigaltstack) \
O(SYS_SETITIMER, setitimer) \ O(SYS_SETITIMER, setitimer) \
O(SYS_POSIX_OPENPT, posix_openpt) \ O(SYS_POSIX_OPENPT, posix_openpt) \
O(SYS_PTSNAME, ptsname) \ O(SYS_PTSNAME, ptsname) \

View File

@ -176,3 +176,10 @@ int sigwait(const sigset_t* __restrict set, int* __restrict sig)
return errno; return errno;
return 0; return 0;
} }
int sigaltstack(const stack_t* __restrict ss, stack_t* __restrict oss)
{
if (syscall(SYS_SIGALTSTACK, ss, oss) == -1)
return errno;
return 0;
}