diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2022-02-26 00:58:06 +0330 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-04 20:07:05 +0100 |
commit | 4bd01b7fe94d81c670420e77ed7bd3cbc3e6caeb (patch) | |
tree | 33312d898d2455031c7b46f8c80bc6d76b413c59 /Kernel | |
parent | 585054d68b0a2e0446cd1cf7205bdc93ca93818f (diff) | |
download | serenity-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.h | 60 | ||||
-rw-r--r-- | Kernel/Arch/mcontext.h | 15 | ||||
-rw-r--r-- | Kernel/Arch/x86/mcontext.h | 58 | ||||
-rw-r--r-- | Kernel/Process.cpp | 60 | ||||
-rw-r--r-- | Kernel/Syscalls/sigaction.cpp | 71 | ||||
-rw-r--r-- | Kernel/Thread.cpp | 135 | ||||
-rw-r--r-- | Kernel/UnixTypes.h | 1 |
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(®isters.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(®isters.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> |