summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2022-02-26 00:58:06 +0330
committerAndreas Kling <kling@serenityos.org>2022-03-04 20:07:05 +0100
commit4bd01b7fe94d81c670420e77ed7bd3cbc3e6caeb (patch)
tree33312d898d2455031c7b46f8c80bc6d76b413c59 /Kernel
parent585054d68b0a2e0446cd1cf7205bdc93ca93818f (diff)
downloadserenity-4bd01b7fe94d81c670420e77ed7bd3cbc3e6caeb.zip
Kernel: Add support for SA_SIGINFO
We currently don't really populate most of the fields, but that can wait :^)
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/API/POSIX/ucontext.h60
-rw-r--r--Kernel/Arch/mcontext.h15
-rw-r--r--Kernel/Arch/x86/mcontext.h58
-rw-r--r--Kernel/Process.cpp60
-rw-r--r--Kernel/Syscalls/sigaction.cpp71
-rw-r--r--Kernel/Thread.cpp135
-rw-r--r--Kernel/UnixTypes.h1
7 files changed, 261 insertions, 139 deletions
diff --git a/Kernel/API/POSIX/ucontext.h b/Kernel/API/POSIX/ucontext.h
new file mode 100644
index 0000000000..306c41cac4
--- /dev/null
+++ b/Kernel/API/POSIX/ucontext.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <Kernel/API/POSIX/sys/types.h>
+#include <Kernel/Arch/mcontext.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct __mcontext mcontext_t;
+
+typedef struct __ucontext {
+ struct __ucontext* uc_link;
+ sigset_t uc_sigmask;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+} ucontext_t;
+
+#define ILL_ILLOPC 0
+#define ILL_ILLOPN 1
+#define ILL_ILLADR 2
+#define ILL_ILLTRP 3
+#define ILL_PRVOPC 4
+#define ILL_PRVREG 5
+#define ILL_COPROC 6
+#define ILL_BADSTK 7
+
+#define FPE_INTDIV 0
+#define FPE_INTOVF 1
+#define FPE_FLTDIV 2
+#define FPE_FLTOVF 3
+#define FPE_FLTUND 4
+#define FPE_FLTRES 5
+#define FPE_FLTINV 6
+
+#define SEGV_MAPERR 0
+#define SEGV_ACCERR 1
+
+#define BUS_ADRALN 0
+#define BUS_ADRERR 1
+#define BUS_OBJERR 2
+
+#define TRAP_BRKPT 0
+#define TRAP_TRACE 1
+
+#define SI_USER 0x40000000
+#define SI_QUEUE 0x40000001
+#define SI_TIMER 0x40000002
+#define SI_ASYNCIO 0x40000003
+#define SI_MESGQ 0x40000004
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Kernel/Arch/mcontext.h b/Kernel/Arch/mcontext.h
new file mode 100644
index 0000000000..ee62f487bd
--- /dev/null
+++ b/Kernel/Arch/mcontext.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2022, the SerenityOS developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Platform.h>
+
+#if ARCH(X86_64) || ARCH(I386)
+# include <Kernel/Arch/x86/mcontext.h>
+#elif ARCH(AARCH64)
+# error "Unknown architecture"
+#endif
diff --git a/Kernel/Arch/x86/mcontext.h b/Kernel/Arch/x86/mcontext.h
new file mode 100644
index 0000000000..a01376ef98
--- /dev/null
+++ b/Kernel/Arch/x86/mcontext.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2022, the SerenityOS developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Platform.h>
+#include <Kernel/API/POSIX/sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct __attribute__((packed)) __mcontext {
+#if ARCH(I386)
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t eip;
+ uint32_t eflags;
+#else
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rbx;
+ uint64_t rsp;
+ uint64_t rbp;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t rip;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rflags;
+#endif
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp
index 6703d83d89..d1b4946748 100644
--- a/Kernel/Process.cpp
+++ b/Kernel/Process.cpp
@@ -293,63 +293,69 @@ void signal_trampoline_dummy()
// blocking syscall, that syscall may return some special error code in eax;
// This error code would likely be overwritten by the signal handler, so it's
// necessary to preserve it here.
+ constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + 5 * sizeof(FlatPtr);
asm(
".intel_syntax noprefix\n"
".globl asm_signal_trampoline\n"
"asm_signal_trampoline:\n"
- // stack state: ret flags, ret ip, register dump, signal mask, signal, handler (alignment = 16), 0
+ // stack state: 0, ucontext, signal_info, (alignment = 16), 0, ucontext*, siginfo*, signal, (alignment = 16), handler
- // save ebp
- "push ebp\n"
- "mov ebp, esp\n"
+ // Pop the handler into ecx
+ "pop ecx\n" // save handler
// we have to save eax 'cause it might be the return value from a syscall
- "push eax\n"
- // align the stack to 16 bytes (as our current offset is 12 from the fake return addr, saved ebp and saved eax)
- "sub esp, 4\n"
- // push the signal code
- "mov eax, [ebp+12]\n"
- "push eax\n"
+ "mov [esp+%P1], eax\n"
+ // Note that the stack is currently aligned to 16 bytes as we popped the extra entries above.
+ // and it's already setup to call the handler with the expected values on the stack.
// call the signal handler
- "call [ebp+8]\n"
- // Unroll stack back to the saved eax
- "add esp, 8\n"
+ "call ecx\n"
+ // drop the 4 arguments
+ "add esp, 16\n"
+ // Current stack state is just saved_eax, ucontext, signal_info.
// syscall SC_sigreturn
"mov eax, %P0\n"
"int 0x82\n"
".globl asm_signal_trampoline_end\n"
"asm_signal_trampoline_end:\n"
- ".att_syntax" ::"i"(Syscall::SC_sigreturn));
+ ".att_syntax"
+ :
+ : "i"(Syscall::SC_sigreturn),
+ "i"(offset_to_first_register_slot));
#elif ARCH(X86_64)
// The trampoline preserves the current rax, pushes the signal code and
// then calls the signal handler. We do this because, when interrupting a
// blocking syscall, that syscall may return some special error code in eax;
// This error code would likely be overwritten by the signal handler, so it's
// necessary to preserve it here.
+ constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + 4 * sizeof(FlatPtr);
asm(
".intel_syntax noprefix\n"
".globl asm_signal_trampoline\n"
"asm_signal_trampoline:\n"
- // stack state: ret flags, ret ip, register dump, signal mask, signal, handler (alignment = 16), 0
+ // stack state: 0, ucontext, signal_info (alignment = 16), ucontext*, siginfo*, signal, handler
- // save rbp
- "push rbp\n"
- "mov rbp, rsp\n"
+ // Pop the handler into rcx
+ "pop rcx\n" // save handler
// we have to save rax 'cause it might be the return value from a syscall
- "push rax\n"
- // align the stack to 16 bytes (our offset is 24 bytes from the fake return addr, saved rbp and saved rax).
- "sub rsp, 8\n"
- // push the signal code
- "mov rdi, [rbp+24]\n"
+ "mov [rsp+%P1], rax\n"
+ // pop signal number into rdi (first param)
+ "pop rdi\n"
+ // pop siginfo* into rsi (second param)
+ "pop rsi\n"
+ // pop ucontext* into rdx (third param)
+ "pop rdx\n"
+ // Note that the stack is currently aligned to 16 bytes as we popped the extra entries above.
// call the signal handler
- "call [rbp+16]\n"
- // unroll stack back to the saved rax
- "add rsp, 8\n"
+ "call rcx\n"
+ // Current stack state is just saved_rax, ucontext, signal_info.
// syscall SC_sigreturn
"mov rax, %P0\n"
"int 0x82\n"
".globl asm_signal_trampoline_end\n"
"asm_signal_trampoline_end:\n"
- ".att_syntax" ::"i"(Syscall::SC_sigreturn));
+ ".att_syntax"
+ :
+ : "i"(Syscall::SC_sigreturn),
+ "i"(offset_to_first_register_slot));
#endif
}
diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp
index f061501b93..b11d3c7a72 100644
--- a/Kernel/Syscalls/sigaction.cpp
+++ b/Kernel/Syscalls/sigaction.cpp
@@ -68,8 +68,6 @@ ErrorOr<FlatPtr> Process::sys$sigaction(int signum, Userspace<const sigaction*>
}
if (user_act) {
auto act = TRY(copy_typed_from_user(user_act));
- if (act.sa_flags & SA_SIGINFO)
- return ENOTSUP;
action.mask = act.sa_mask;
action.flags = act.sa_flags;
action.handler_or_sigaction = VirtualAddress { reinterpret_cast<void*>(act.sa_sigaction) };
@@ -83,63 +81,36 @@ ErrorOr<FlatPtr> Process::sys$sigreturn([[maybe_unused]] RegisterState& register
TRY(require_promise(Pledge::stdio));
SmapDisabler disabler;
-#if ARCH(I386)
- // Stack state (created by the signal trampoline):
- // ret flags, ret ip, register dump,
- // signal mask, signal, handler (alignment = 16),
- // 0, ebp, eax
-
// Here, we restore the state pushed by dispatch signal and asm_signal_trampoline.
- FlatPtr* stack_ptr = bit_cast<FlatPtr*>(registers.userspace_esp);
- FlatPtr smuggled_eax = *stack_ptr;
-
- // pop the stored eax, ebp, return address, handler and signal code
- stack_ptr += 5;
-
- Thread::current()->m_signal_mask = *stack_ptr;
- stack_ptr++;
-
- // pop edi, esi, ebp, esp, ebx, edx, ecx and eax
- memcpy(&registers.edi, stack_ptr, 8 * sizeof(FlatPtr));
- stack_ptr += 8;
-
- registers.eip = *stack_ptr;
- stack_ptr++;
+ auto stack_ptr = registers.userspace_sp();
- registers.eflags = (registers.eflags & ~safe_eflags_mask) | (*stack_ptr & safe_eflags_mask);
- stack_ptr++;
-
- registers.userspace_esp = registers.esp;
- return smuggled_eax;
-#else
// Stack state (created by the signal trampoline):
- // ret flags, ret ip, register dump,
- // signal mask, signal, handler (alignment = 16),
- // 0, ebp, eax
-
- // Here, we restore the state pushed by dispatch signal and asm_signal_trampoline.
- FlatPtr* stack_ptr = (FlatPtr*)registers.userspace_rsp;
- FlatPtr smuggled_rax = *stack_ptr;
+ // saved_ax, ucontext, signal_info.
+ stack_ptr += sizeof(siginfo); // We don't need this here.
- // pop the stored rax, rbp, return address, handler and signal code
- stack_ptr += 5;
+ auto ucontext = TRY(copy_typed_from_user<__ucontext>(stack_ptr));
+ stack_ptr += sizeof(__ucontext);
- Thread::current()->m_signal_mask = *stack_ptr;
- stack_ptr++;
+ auto saved_ax = TRY(copy_typed_from_user<FlatPtr>(stack_ptr));
- // pop rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax, r8, r9, r10, r11, r12, r13, r14 and r15
- memcpy(&registers.rdi, stack_ptr, 16 * sizeof(FlatPtr));
- stack_ptr += 16;
-
- registers.rip = *stack_ptr;
- stack_ptr++;
+ Thread::current()->m_signal_mask = ucontext.uc_sigmask;
+#if ARCH(X86_64)
+ auto sp = registers.rsp;
+#elif ARCH(I386)
+ auto sp = registers.esp;
+#endif
- registers.rflags = (registers.rflags & ~safe_eflags_mask) | (*stack_ptr & safe_eflags_mask);
- stack_ptr++;
+ copy_ptrace_registers_into_kernel_registers(registers, static_cast<PtraceRegisters const&>(ucontext.uc_mcontext));
- registers.userspace_rsp = registers.rsp;
- return smuggled_rax;
+#if ARCH(X86_64)
+ registers.set_userspace_sp(registers.rsp);
+ registers.rsp = sp;
+#elif ARCH(I386)
+ registers.set_userspace_sp(registers.esp);
+ registers.esp = sp;
#endif
+
+ return saved_ax;
}
ErrorOr<void> Process::remap_range_as_stack(FlatPtr address, size_t size)
diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp
index ccc8ff6a33..837741fc50 100644
--- a/Kernel/Thread.cpp
+++ b/Kernel/Thread.cpp
@@ -938,6 +938,13 @@ static ErrorOr<void> push_value_on_user_stack(FlatPtr& stack, FlatPtr data)
return copy_to_user((FlatPtr*)stack, &data);
}
+template<typename T>
+static ErrorOr<void> copy_value_on_user_stack(FlatPtr& stack, T const& data)
+{
+ stack -= sizeof(data);
+ return copy_to_user((RemoveCVReference<T>*)stack, &data);
+}
+
void Thread::resume_from_stopped()
{
VERIFY(is_stopped());
@@ -976,8 +983,6 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
}
auto& action = m_process->m_signal_action_data[signal];
- // FIXME: Implement SA_SIGINFO signal handlers.
- VERIFY(!(action.flags & SA_SIGINFO));
if (!current_trap() && !action.handler_or_sigaction.is_null()) {
// We're trying dispatch a handled signal to a user process that was scheduled
@@ -1057,83 +1062,89 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
bool use_alternative_stack = ((action.flags & SA_ONSTACK) != 0) && has_alternative_signal_stack() && !is_in_alternative_signal_stack();
auto setup_stack = [&](RegisterState& state) -> ErrorOr<void> {
- FlatPtr old_sp = state.userspace_sp();
FlatPtr stack;
if (use_alternative_stack)
stack = m_alternative_signal_stack + m_alternative_signal_stack_size;
else
- stack = old_sp;
+ stack = state.userspace_sp();
+
+ dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to IP {:p}, SP {:p}", state.ip(), state.userspace_sp());
+
+ __ucontext ucontext {
+ .uc_link = nullptr,
+ .uc_sigmask = old_signal_mask,
+ .uc_stack = {
+ .ss_sp = nullptr,
+ .ss_flags = 0,
+ .ss_size = 0,
+ },
+ .uc_mcontext = {},
+ };
+ copy_kernel_registers_into_ptrace_registers(static_cast<PtraceRegisters&>(ucontext.uc_mcontext), state);
+
+ siginfo signal_info {
+ .si_signo = signal,
+ .si_code = 0, // FIXME: Signal-specific value, fill this in.
+ .si_errno = 0,
+ // FIXME: Plumb sender information here.
+ .si_pid = 0,
+ .si_uid = 0,
+ // FIXME: Fill these in.
+ .si_addr = 0,
+ .si_status = 0,
+ .si_band = 0,
+ .si_value = {
+ .sival_int = 0,
+ },
+ };
- FlatPtr ret_ip = state.ip();
- FlatPtr ret_flags = state.flags();
-
- dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to IP {:p}, SP {:p}", ret_ip, old_sp);
-
- FlatPtr start_of_stack;
#if ARCH(I386)
- // Align the stack to 16 bytes.
- // Note that we push some elements on to the stack before the return address,
- // so we need to account for this here.
- constexpr static FlatPtr elements_pushed_on_stack_before_return_address = 13;
- FlatPtr stack_alignment = (stack - elements_pushed_on_stack_before_return_address * sizeof(FlatPtr)) % 16;
- stack -= stack_alignment;
- start_of_stack = stack;
-
- TRY(push_value_on_user_stack(stack, ret_flags));
-
- TRY(push_value_on_user_stack(stack, ret_ip));
- TRY(push_value_on_user_stack(stack, state.eax));
- TRY(push_value_on_user_stack(stack, state.ecx));
- TRY(push_value_on_user_stack(stack, state.edx));
- TRY(push_value_on_user_stack(stack, state.ebx));
- TRY(push_value_on_user_stack(stack, old_sp));
- TRY(push_value_on_user_stack(stack, state.ebp));
- TRY(push_value_on_user_stack(stack, state.esi));
- TRY(push_value_on_user_stack(stack, state.edi));
+ constexpr static FlatPtr thread_red_zone_size = 0;
+#elif ARCH(X86_64)
+ constexpr static FlatPtr thread_red_zone_size = 128;
#else
+# error Unknown architecture in dispatch_signal
+#endif
+
// Align the stack to 16 bytes.
// Note that we push some elements on to the stack before the return address,
// so we need to account for this here.
- // We also are not allowed to touch the thread's red-zone of 128 bytes
- constexpr static FlatPtr elements_pushed_on_stack_before_return_address = 21;
- FlatPtr stack_alignment = (stack - elements_pushed_on_stack_before_return_address * sizeof(FlatPtr)) % 16;
- stack -= 128 + stack_alignment;
- start_of_stack = stack;
-
- TRY(push_value_on_user_stack(stack, ret_flags));
-
- TRY(push_value_on_user_stack(stack, ret_ip));
- TRY(push_value_on_user_stack(stack, state.r15));
- TRY(push_value_on_user_stack(stack, state.r14));
- TRY(push_value_on_user_stack(stack, state.r13));
- TRY(push_value_on_user_stack(stack, state.r12));
- TRY(push_value_on_user_stack(stack, state.r11));
- TRY(push_value_on_user_stack(stack, state.r10));
- TRY(push_value_on_user_stack(stack, state.r9));
- TRY(push_value_on_user_stack(stack, state.r8));
- TRY(push_value_on_user_stack(stack, state.rax));
- TRY(push_value_on_user_stack(stack, state.rcx));
- TRY(push_value_on_user_stack(stack, state.rdx));
- TRY(push_value_on_user_stack(stack, state.rbx));
- TRY(push_value_on_user_stack(stack, old_sp));
- TRY(push_value_on_user_stack(stack, state.rbp));
- TRY(push_value_on_user_stack(stack, state.rsi));
- TRY(push_value_on_user_stack(stack, state.rdi));
-#endif
+ constexpr static FlatPtr elements_pushed_on_stack_before_handler_address = 1; // one slot for a saved register
+ FlatPtr const extra_bytes_pushed_on_stack_before_handler_address = sizeof(ucontext) + sizeof(signal_info);
+ FlatPtr stack_alignment = (stack - elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) + extra_bytes_pushed_on_stack_before_handler_address) % 16;
+ // Also note that we have to skip the thread red-zone (if needed), so do that here.
+ stack -= thread_red_zone_size + stack_alignment;
+ auto start_of_stack = stack;
- // PUSH old_signal_mask
- TRY(push_value_on_user_stack(stack, old_signal_mask));
+ TRY(push_value_on_user_stack(stack, 0)); // syscall return value slot
- TRY(push_value_on_user_stack(stack, signal));
- TRY(push_value_on_user_stack(stack, handler_vaddr.get()));
+ TRY(copy_value_on_user_stack(stack, ucontext));
+ auto pointer_to_ucontext = stack;
+
+ TRY(copy_value_on_user_stack(stack, signal_info));
+ auto pointer_to_signal_info = stack;
// Make sure we actually pushed as many elements as we claimed to have pushed.
- if (start_of_stack - stack != elements_pushed_on_stack_before_return_address * sizeof(FlatPtr))
- PANIC("Stack in invalid state after signal trampoline, expected {:x} but got {:x}", start_of_stack - elements_pushed_on_stack_before_return_address * sizeof(FlatPtr), stack);
+ if (start_of_stack - stack != elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) + extra_bytes_pushed_on_stack_before_handler_address) {
+ PANIC("Stack in invalid state after signal trampoline, expected {:x} but got {:x}",
+ start_of_stack - elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) - extra_bytes_pushed_on_stack_before_handler_address, stack);
+ }
+
+ VERIFY(stack % 16 == 0);
+
+#if ARCH(I386)
+ // Leave one empty slot to align the stack for a handler call.
+ TRY(push_value_on_user_stack(stack, 0));
+#endif
+ TRY(push_value_on_user_stack(stack, pointer_to_ucontext));
+ TRY(push_value_on_user_stack(stack, pointer_to_signal_info));
+ TRY(push_value_on_user_stack(stack, signal));
+#if ARCH(I386)
VERIFY(stack % 16 == 0);
+#endif
- TRY(push_value_on_user_stack(stack, 0)); // push fake return address
+ TRY(push_value_on_user_stack(stack, handler_vaddr.get()));
// We write back the adjusted stack value into the register state.
// We have to do this because we can't just pass around a reference to a packed field, as it's UB.
diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h
index 586b3ae0c8..05b33873a8 100644
--- a/Kernel/UnixTypes.h
+++ b/Kernel/UnixTypes.h
@@ -31,4 +31,5 @@
#include <Kernel/API/POSIX/sys/wait.h>
#include <Kernel/API/POSIX/termios.h>
#include <Kernel/API/POSIX/time.h>
+#include <Kernel/API/POSIX/ucontext.h>
#include <Kernel/API/POSIX/unistd.h>