Kernel: Clean up signal handling
We now appreciate sa_mask and SA_NODEFER and change the signal mask for the duration of signal handler. This is done by making a sigprocmask syscall at the end of the signal handler. Back-to-back signals will still grow stack as original registers are popped AFTER the block mask is updated. I guess this is why linux has sigreturn(?).
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
.section .userspace, "ax"
|
.section .userspace, "ax"
|
||||||
|
|
||||||
// stack contains
|
// stack contains
|
||||||
// return address
|
// (4 bytes) return address
|
||||||
// return stack
|
// (4 bytes) return stack
|
||||||
// return rflags
|
// (4 bytes) return rflags
|
||||||
// siginfo_t
|
// (8 bytes) restore sigmask
|
||||||
// signal number
|
// (36 bytes) siginfo_t
|
||||||
// signal handler
|
// (4 bytes) signal number
|
||||||
|
// (4 bytes) signal handler
|
||||||
|
|
||||||
.global signal_trampoline
|
.global signal_trampoline
|
||||||
signal_trampoline:
|
signal_trampoline:
|
||||||
@@ -53,6 +54,13 @@ signal_trampoline:
|
|||||||
movl %ebp, %esp
|
movl %ebp, %esp
|
||||||
addl $24, %esp
|
addl $24, %esp
|
||||||
|
|
||||||
|
// restore sigmask
|
||||||
|
movl $83, %eax // SYS_SIGPROCMASK
|
||||||
|
movl $3, %ebx // SIG_SETMASK
|
||||||
|
leal 72(%esp), %ecx // set
|
||||||
|
xorl %edx, %edx // oset
|
||||||
|
int $0xF0
|
||||||
|
|
||||||
// restore registers
|
// restore registers
|
||||||
popl %ebp
|
popl %ebp
|
||||||
popl %eax
|
popl %eax
|
||||||
@@ -62,8 +70,8 @@ signal_trampoline:
|
|||||||
popl %edi
|
popl %edi
|
||||||
popl %esi
|
popl %esi
|
||||||
|
|
||||||
// skip handler, number, siginfo_t
|
// skip handler, number, siginfo_t, sigmask
|
||||||
addl $44, %esp
|
addl $52, %esp
|
||||||
|
|
||||||
// restore flags
|
// restore flags
|
||||||
popf
|
popf
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
.section .userspace, "ax"
|
.section .userspace, "ax"
|
||||||
|
|
||||||
// stack contains
|
// stack contains
|
||||||
// return address
|
// (8 bytes) return address
|
||||||
// return stack
|
// (8 bytes) return stack
|
||||||
// return rflags
|
// (8 bytes) return rflags
|
||||||
// siginfo_t
|
// (8 bytes) restore sigmask
|
||||||
// signal number
|
// (56 bytes) siginfo_t
|
||||||
// signal handler
|
// (8 bytes) signal number
|
||||||
|
// (8 bytes) signal handler
|
||||||
|
|
||||||
.global signal_trampoline
|
.global signal_trampoline
|
||||||
signal_trampoline:
|
signal_trampoline:
|
||||||
@@ -55,6 +56,13 @@ signal_trampoline:
|
|||||||
movq %rbp, %rsp
|
movq %rbp, %rsp
|
||||||
addq $40, %rsp
|
addq $40, %rsp
|
||||||
|
|
||||||
|
// restore sigmask
|
||||||
|
movq $83, %rdi // SYS_SIGPROCMASK
|
||||||
|
movq $3, %rsi // SIG_SETMASK
|
||||||
|
leaq 192(%rsp), %rdx // set
|
||||||
|
xorq %r10, %r10 // oset
|
||||||
|
syscall
|
||||||
|
|
||||||
// restore registers
|
// restore registers
|
||||||
popq %rbp
|
popq %rbp
|
||||||
popq %rax
|
popq %rax
|
||||||
@@ -72,13 +80,13 @@ signal_trampoline:
|
|||||||
popq %r14
|
popq %r14
|
||||||
popq %r15
|
popq %r15
|
||||||
|
|
||||||
// skip handler, number, siginfo_t
|
// skip handler, number, siginfo_t, sigmask
|
||||||
addq $72, %rsp
|
addq $80, %rsp
|
||||||
|
|
||||||
// restore flags
|
// restore flags
|
||||||
popfq
|
popfq
|
||||||
|
|
||||||
movq (%rsp), %rsp
|
movq (%rsp), %rsp
|
||||||
|
|
||||||
// return over red-zone and siginfo_t
|
// return over red-zone
|
||||||
ret $128
|
ret $128
|
||||||
|
|||||||
@@ -157,12 +157,13 @@ namespace Kernel
|
|||||||
|
|
||||||
struct signal_handle_info_t
|
struct signal_handle_info_t
|
||||||
{
|
{
|
||||||
vaddr_t signal_handler;
|
vaddr_t handler;
|
||||||
vaddr_t signal_stack_top;
|
vaddr_t stack_top;
|
||||||
|
uint64_t restore_sigmask;
|
||||||
bool has_sa_restart;
|
bool has_sa_restart;
|
||||||
};
|
};
|
||||||
signal_handle_info_t remove_signal_and_get_info(int signal);
|
signal_handle_info_t remove_signal_and_get_info(int signal);
|
||||||
void handle_signal_impl(int signal, const siginfo_t&, vaddr_t signal_handler, vaddr_t signal_stack_top);
|
void handle_signal_impl(int signal, const siginfo_t&, const signal_handle_info_t&);
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -632,12 +632,7 @@ namespace Kernel
|
|||||||
handle_info = remove_signal_and_get_info(signal);
|
handle_info = remove_signal_and_get_info(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_signal_impl(
|
handle_signal_impl(signal, signal_info, handle_info);
|
||||||
signal,
|
|
||||||
signal_info,
|
|
||||||
handle_info.signal_handler,
|
|
||||||
handle_info.signal_stack_top
|
|
||||||
);
|
|
||||||
|
|
||||||
return handle_info.has_sa_restart;
|
return handle_info.has_sa_restart;
|
||||||
}
|
}
|
||||||
@@ -652,18 +647,29 @@ namespace Kernel
|
|||||||
|
|
||||||
signal_handle_info_t handle_info;
|
signal_handle_info_t handle_info;
|
||||||
|
|
||||||
|
// If this signal is blocked or ignored, terminate the process
|
||||||
|
bool terminate_process = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
SpinLockGuard _1(m_signal_lock);
|
SpinLockGuard _1(m_signal_lock);
|
||||||
SpinLockGuard _2(m_process->m_signal_lock);
|
SpinLockGuard _2(m_process->m_signal_lock);
|
||||||
|
|
||||||
|
if (m_signal_block_mask & (1ull << signal))
|
||||||
|
terminate_process = true;
|
||||||
|
|
||||||
handle_info = remove_signal_and_get_info(signal);
|
handle_info = remove_signal_and_get_info(signal);
|
||||||
|
|
||||||
|
if (handle_info.handler == reinterpret_cast<vaddr_t>(SIG_IGN))
|
||||||
|
terminate_process = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_signal_impl(
|
if (terminate_process)
|
||||||
signal,
|
{
|
||||||
signal_info,
|
process().exit(128 + signal, signal | 0x80);
|
||||||
handle_info.signal_handler,
|
ASSERT_NOT_REACHED();
|
||||||
handle_info.signal_stack_top
|
}
|
||||||
);
|
|
||||||
|
handle_signal_impl(signal, signal_info, handle_info);
|
||||||
|
|
||||||
return handle_info.has_sa_restart;
|
return handle_info.has_sa_restart;
|
||||||
}
|
}
|
||||||
@@ -676,6 +682,8 @@ namespace Kernel
|
|||||||
ASSERT(signal >= _SIGMIN);
|
ASSERT(signal >= _SIGMIN);
|
||||||
ASSERT(signal <= _SIGMAX);
|
ASSERT(signal <= _SIGMAX);
|
||||||
|
|
||||||
|
const uint64_t restore_sigmask = m_signal_block_mask;
|
||||||
|
|
||||||
auto& handler = m_process->m_signal_handlers[signal];
|
auto& handler = m_process->m_signal_handlers[signal];
|
||||||
|
|
||||||
const vaddr_t signal_handler = (handler.sa_flags & SA_SIGINFO)
|
const vaddr_t signal_handler = (handler.sa_flags & SA_SIGINFO)
|
||||||
@@ -687,6 +695,10 @@ namespace Kernel
|
|||||||
if (m_signal_alt_stack.ss_flags != SS_DISABLE && (handler.sa_flags & SA_ONSTACK) && !currently_on_alternate_stack())
|
if (m_signal_alt_stack.ss_flags != SS_DISABLE && (handler.sa_flags & SA_ONSTACK) && !currently_on_alternate_stack())
|
||||||
signal_stack_top = reinterpret_cast<vaddr_t>(m_signal_alt_stack.ss_sp) + m_signal_alt_stack.ss_size;
|
signal_stack_top = reinterpret_cast<vaddr_t>(m_signal_alt_stack.ss_sp) + m_signal_alt_stack.ss_size;
|
||||||
|
|
||||||
|
m_signal_block_mask |= handler.sa_mask;
|
||||||
|
if (!(handler.sa_flags & SA_NODEFER))
|
||||||
|
m_signal_block_mask |= 1ull << signal;
|
||||||
|
|
||||||
if (handler.sa_flags & SA_RESETHAND)
|
if (handler.sa_flags & SA_RESETHAND)
|
||||||
handler = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
handler = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
|
||||||
|
|
||||||
@@ -700,13 +712,14 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.signal_handler = signal_handler,
|
.handler = signal_handler,
|
||||||
.signal_stack_top = signal_stack_top,
|
.stack_top = signal_stack_top,
|
||||||
|
.restore_sigmask = restore_sigmask,
|
||||||
.has_sa_restart = has_sa_restart,
|
.has_sa_restart = has_sa_restart,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::handle_signal_impl(int signal, const siginfo_t& signal_info, vaddr_t signal_handler, vaddr_t signal_stack_top)
|
void Thread::handle_signal_impl(int signal, const siginfo_t& signal_info, const signal_handle_info_t& handle_info)
|
||||||
{
|
{
|
||||||
ASSERT(this == &Thread::current());
|
ASSERT(this == &Thread::current());
|
||||||
ASSERT(is_userspace());
|
ASSERT(is_userspace());
|
||||||
@@ -715,64 +728,39 @@ namespace Kernel
|
|||||||
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));
|
||||||
|
|
||||||
if (signal_handler == (vaddr_t)SIG_IGN)
|
if (handle_info.handler == reinterpret_cast<vaddr_t>(SIG_IGN))
|
||||||
;
|
;
|
||||||
else if (signal_handler != (vaddr_t)SIG_DFL)
|
else if (handle_info.handler != reinterpret_cast<vaddr_t>(SIG_DFL))
|
||||||
{
|
{
|
||||||
// call userspace signal handlers
|
// call userspace signal handlers
|
||||||
#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
|
||||||
|
|
||||||
|
const auto write_to_stack =
|
||||||
|
[&]<typename T>(uintptr_t& sp, const T& value)
|
||||||
{
|
{
|
||||||
// Make sure stack is allocated
|
static_assert(sizeof(T) >= sizeof(uintptr_t));
|
||||||
|
sp -= sizeof(T);
|
||||||
vaddr_t pages[3] {};
|
if (m_process->write_to_user(reinterpret_cast<void*>(sp), &value, sizeof(T)).is_error())
|
||||||
size_t page_count { 0 };
|
m_process->exit(128 + SIGSEGV, SIGSEGV | 0x80);
|
||||||
|
};
|
||||||
if (signal_stack_top == 0)
|
|
||||||
{
|
|
||||||
pages[0] = (interrupt_stack.sp - 1 * sizeof(uintptr_t) ) & PAGE_ADDR_MASK;
|
|
||||||
pages[1] = (interrupt_stack.sp - 5 * sizeof(uintptr_t) - sizeof(siginfo_t)) & PAGE_ADDR_MASK;
|
|
||||||
page_count = 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pages[0] = (interrupt_stack.sp - 1 * sizeof(uintptr_t) ) & PAGE_ADDR_MASK;
|
|
||||||
pages[2] = (signal_stack_top - 1 * sizeof(uintptr_t) ) & PAGE_ADDR_MASK;
|
|
||||||
pages[1] = (signal_stack_top - 4 * sizeof(uintptr_t) - sizeof(siginfo_t)) & PAGE_ADDR_MASK;
|
|
||||||
page_count = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < page_count; i++)
|
|
||||||
{
|
|
||||||
if (m_process->page_table().get_page_flags(pages[i]) & PageTable::Flags::Present)
|
|
||||||
continue;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
const vaddr_t old_stack = interrupt_stack.sp;
|
||||||
if (signal_stack_top)
|
if (handle_info.stack_top)
|
||||||
interrupt_stack.sp = signal_stack_top;
|
interrupt_stack.sp = handle_info.stack_top;
|
||||||
write_to_stack(interrupt_stack.sp, old_stack);
|
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, handle_info.restore_sigmask);
|
||||||
|
|
||||||
{
|
siginfo_t copy = signal_info;
|
||||||
interrupt_stack.sp -= sizeof(siginfo_t);
|
copy.si_signo = signal;
|
||||||
|
copy.si_addr = reinterpret_cast<void*>(interrupt_stack.ip);
|
||||||
|
write_to_stack(interrupt_stack.sp, copy);
|
||||||
|
|
||||||
auto& stack_signal_info = *reinterpret_cast<siginfo_t*>(interrupt_stack.sp);
|
write_to_stack(interrupt_stack.sp, static_cast<uintptr_t>(signal));
|
||||||
stack_signal_info = signal_info;
|
write_to_stack(interrupt_stack.sp, handle_info.handler);
|
||||||
stack_signal_info.si_signo = signal;
|
|
||||||
stack_signal_info.si_addr = reinterpret_cast<void*>(interrupt_stack.ip);
|
|
||||||
|
|
||||||
static_assert(sizeof(siginfo_t) % sizeof(uintptr_t) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_to_stack(interrupt_stack.sp, signal);
|
|
||||||
write_to_stack(interrupt_stack.sp, signal_handler);
|
|
||||||
interrupt_stack.ip = (uintptr_t)signal_trampoline;
|
interrupt_stack.ip = (uintptr_t)signal_trampoline;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user