From 59cab85002f0ae950c1325b093971945be73fae7 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 24 Feb 2023 22:18:34 +0200 Subject: Kernel: Rename Syscall.cpp => Syscalls/SyscallHandler.cpp --- Kernel/CMakeLists.txt | 4 +- Kernel/Syscall.cpp | 194 ------------------------------------- Kernel/Syscalls/SyscallHandler.cpp | 194 +++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 196 deletions(-) delete mode 100644 Kernel/Syscall.cpp create mode 100644 Kernel/Syscalls/SyscallHandler.cpp diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index c8d736ebc7..9334b7679d 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -258,7 +258,6 @@ set(KERNEL_SOURCES Net/Socket.cpp Net/TCPSocket.cpp Net/UDPSocket.cpp - Syscall.cpp Security/AddressSanitizer.cpp Security/Credentials.cpp Security/Random.cpp @@ -324,6 +323,7 @@ set(KERNEL_SOURCES Syscalls/stat.cpp Syscalls/statvfs.cpp Syscalls/sync.cpp + Syscalls/SyscallHandler.cpp Syscalls/sysconf.cpp Syscalls/thread.cpp Syscalls/times.cpp @@ -653,7 +653,7 @@ if (ENABLE_KERNEL_COVERAGE_COLLECTION) # interrupt handlers because their calling convention is not compatible # with the System V ABI. ../Kernel/Arch/x86_64/Interrupts.cpp - ../Kernel/Syscall.cpp + ../Kernel/Syscall/SyscallHandler.cpp ) set_source_files_properties(${KCOV_EXCLUDED_SOURCES} PROPERTIES COMPILE_FLAGS "-fno-sanitize-coverage=trace-pc") elseif (ENABLE_USERSPACE_COVERAGE_COLLECTION) diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp deleted file mode 100644 index c6130882cb..0000000000 --- a/Kernel/Syscall.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -namespace Syscall { - -using Handler = auto(Process::*)(FlatPtr, FlatPtr, FlatPtr, FlatPtr) -> ErrorOr; -using HandlerWithRegisterState = auto(Process::*)(RegisterState&) -> ErrorOr; - -struct HandlerMetadata { - Handler handler; - NeedsBigProcessLock needs_lock; -}; - -#define __ENUMERATE_SYSCALL(sys_call, needs_lock) { bit_cast(&Process::sys$##sys_call), needs_lock }, -static const HandlerMetadata s_syscall_table[] = { - ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) -}; -#undef __ENUMERATE_SYSCALL - -ErrorOr handle(RegisterState& regs, FlatPtr function, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3, FlatPtr arg4) -{ - VERIFY_INTERRUPTS_ENABLED(); - auto* current_thread = Thread::current(); - auto& process = current_thread->process(); - current_thread->did_syscall(); - - PerformanceManager::add_syscall_event(*current_thread, regs); - - if (function >= Function::__Count) { - dbgln("Unknown syscall {} requested ({:p}, {:p}, {:p}, {:p})", function, arg1, arg2, arg3, arg4); - return ENOSYS; - } - - auto const syscall_metadata = s_syscall_table[function]; - if (syscall_metadata.handler == nullptr) { - dbgln("Null syscall {} requested, you probably need to rebuild this program!", function); - return ENOSYS; - } - - MutexLocker mutex_locker; - auto const needs_big_lock = syscall_metadata.needs_lock == NeedsBigProcessLock::Yes; - if (needs_big_lock) { - mutex_locker.attach_and_lock(process.big_lock()); - }; - - if (function == SC_exit || function == SC_exit_thread) { - // These syscalls need special handling since they never return to the caller. - // In these cases the process big lock will get released on the exit of the thread. - - if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { - regs.set_return_reg(0); - tracer->set_trace_syscalls(false); - process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! - } - - switch (function) { - case SC_exit: - process.sys$exit(arg1); - case SC_exit_thread: - process.sys$exit_thread(arg1, arg2, arg3); - default: - VERIFY_NOT_REACHED(); - } - } - - ErrorOr result { FlatPtr(nullptr) }; - if (function == SC_fork || function == SC_sigreturn) { - // These syscalls want the RegisterState& rather than individual parameters. - auto handler = bit_cast(syscall_metadata.handler); - result = (process.*(handler))(regs); - } else { - result = (process.*(syscall_metadata.handler))(arg1, arg2, arg3, arg4); - } - - return result; -} - -} - -extern "C" NEVER_INLINE void syscall_handler(TrapFrame* trap); -NEVER_INLINE void syscall_handler(TrapFrame* trap) -{ -#if ARCH(X86_64) - // Make sure SMAP protection is enabled on syscall entry. - clac(); -#elif ARCH(AARCH64) - // FIXME: Implement the security mechanism for aarch64 -#else -# error Unknown architecture -#endif - - auto& regs = *trap->regs; - auto* current_thread = Thread::current(); - VERIFY(current_thread->previous_mode() == ExecutionMode::User); - auto& process = current_thread->process(); - if (process.is_dying()) { - // It's possible this thread is just about to make a syscall while another is - // is killing our process. - current_thread->die_if_needed(); - return; - } - - if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { - tracer->set_trace_syscalls(false); - process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! - } - - current_thread->yield_if_stopped(); - -#if ARCH(X86_64) - // Apply a random offset in the range 0-255 to the stack pointer, - // to make kernel stacks a bit less deterministic. - u32 lsw; - u32 msw; - read_tsc(lsw, msw); - - auto* ptr = (char*)__builtin_alloca(lsw & 0xff); - asm volatile("" - : "=m"(*ptr)); - - constexpr FlatPtr iopl_mask = 3u << 12; - - FlatPtr flags = regs.flags(); - if ((flags & (iopl_mask)) != 0) { - PANIC("Syscall from process with IOPL != 0"); - } -#elif ARCH(AARCH64) - // FIXME: Implement the security mechanism for aarch64 -#else -# error Unknown architecture -#endif - - Memory::MemoryManager::validate_syscall_preconditions(process, regs); - - FlatPtr function; - FlatPtr arg1; - FlatPtr arg2; - FlatPtr arg3; - FlatPtr arg4; - regs.capture_syscall_params(function, arg1, arg2, arg3, arg4); - - auto result = Syscall::handle(regs, function, arg1, arg2, arg3, arg4); - - if (result.is_error()) { - regs.set_return_reg(-result.error().code()); - } else { - regs.set_return_reg(result.value()); - } - - if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { - tracer->set_trace_syscalls(false); - process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! - } - - current_thread->yield_if_stopped(); - - current_thread->check_dispatch_pending_signal(); - - // If the previous mode somehow changed something is seriously messed up... - VERIFY(current_thread->previous_mode() == ExecutionMode::User); - - // Check if we're supposed to return to userspace or just die. - current_thread->die_if_needed(); - - // Crash any processes which have committed a promise violation during syscall handling. - if (result.is_error() && result.error().code() == EPROMISEVIOLATION) { - VERIFY(current_thread->is_promise_violation_pending()); - current_thread->set_promise_violation_pending(false); - process.crash(SIGABRT, {}); - } else { - VERIFY(!current_thread->is_promise_violation_pending()); - } - - VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); -} - -} diff --git a/Kernel/Syscalls/SyscallHandler.cpp b/Kernel/Syscalls/SyscallHandler.cpp new file mode 100644 index 0000000000..c6130882cb --- /dev/null +++ b/Kernel/Syscalls/SyscallHandler.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +namespace Syscall { + +using Handler = auto(Process::*)(FlatPtr, FlatPtr, FlatPtr, FlatPtr) -> ErrorOr; +using HandlerWithRegisterState = auto(Process::*)(RegisterState&) -> ErrorOr; + +struct HandlerMetadata { + Handler handler; + NeedsBigProcessLock needs_lock; +}; + +#define __ENUMERATE_SYSCALL(sys_call, needs_lock) { bit_cast(&Process::sys$##sys_call), needs_lock }, +static const HandlerMetadata s_syscall_table[] = { + ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) +}; +#undef __ENUMERATE_SYSCALL + +ErrorOr handle(RegisterState& regs, FlatPtr function, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3, FlatPtr arg4) +{ + VERIFY_INTERRUPTS_ENABLED(); + auto* current_thread = Thread::current(); + auto& process = current_thread->process(); + current_thread->did_syscall(); + + PerformanceManager::add_syscall_event(*current_thread, regs); + + if (function >= Function::__Count) { + dbgln("Unknown syscall {} requested ({:p}, {:p}, {:p}, {:p})", function, arg1, arg2, arg3, arg4); + return ENOSYS; + } + + auto const syscall_metadata = s_syscall_table[function]; + if (syscall_metadata.handler == nullptr) { + dbgln("Null syscall {} requested, you probably need to rebuild this program!", function); + return ENOSYS; + } + + MutexLocker mutex_locker; + auto const needs_big_lock = syscall_metadata.needs_lock == NeedsBigProcessLock::Yes; + if (needs_big_lock) { + mutex_locker.attach_and_lock(process.big_lock()); + }; + + if (function == SC_exit || function == SC_exit_thread) { + // These syscalls need special handling since they never return to the caller. + // In these cases the process big lock will get released on the exit of the thread. + + if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { + regs.set_return_reg(0); + tracer->set_trace_syscalls(false); + process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! + } + + switch (function) { + case SC_exit: + process.sys$exit(arg1); + case SC_exit_thread: + process.sys$exit_thread(arg1, arg2, arg3); + default: + VERIFY_NOT_REACHED(); + } + } + + ErrorOr result { FlatPtr(nullptr) }; + if (function == SC_fork || function == SC_sigreturn) { + // These syscalls want the RegisterState& rather than individual parameters. + auto handler = bit_cast(syscall_metadata.handler); + result = (process.*(handler))(regs); + } else { + result = (process.*(syscall_metadata.handler))(arg1, arg2, arg3, arg4); + } + + return result; +} + +} + +extern "C" NEVER_INLINE void syscall_handler(TrapFrame* trap); +NEVER_INLINE void syscall_handler(TrapFrame* trap) +{ +#if ARCH(X86_64) + // Make sure SMAP protection is enabled on syscall entry. + clac(); +#elif ARCH(AARCH64) + // FIXME: Implement the security mechanism for aarch64 +#else +# error Unknown architecture +#endif + + auto& regs = *trap->regs; + auto* current_thread = Thread::current(); + VERIFY(current_thread->previous_mode() == ExecutionMode::User); + auto& process = current_thread->process(); + if (process.is_dying()) { + // It's possible this thread is just about to make a syscall while another is + // is killing our process. + current_thread->die_if_needed(); + return; + } + + if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { + tracer->set_trace_syscalls(false); + process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! + } + + current_thread->yield_if_stopped(); + +#if ARCH(X86_64) + // Apply a random offset in the range 0-255 to the stack pointer, + // to make kernel stacks a bit less deterministic. + u32 lsw; + u32 msw; + read_tsc(lsw, msw); + + auto* ptr = (char*)__builtin_alloca(lsw & 0xff); + asm volatile("" + : "=m"(*ptr)); + + constexpr FlatPtr iopl_mask = 3u << 12; + + FlatPtr flags = regs.flags(); + if ((flags & (iopl_mask)) != 0) { + PANIC("Syscall from process with IOPL != 0"); + } +#elif ARCH(AARCH64) + // FIXME: Implement the security mechanism for aarch64 +#else +# error Unknown architecture +#endif + + Memory::MemoryManager::validate_syscall_preconditions(process, regs); + + FlatPtr function; + FlatPtr arg1; + FlatPtr arg2; + FlatPtr arg3; + FlatPtr arg4; + regs.capture_syscall_params(function, arg1, arg2, arg3, arg4); + + auto result = Syscall::handle(regs, function, arg1, arg2, arg3, arg4); + + if (result.is_error()) { + regs.set_return_reg(-result.error().code()); + } else { + regs.set_return_reg(result.value()); + } + + if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { + tracer->set_trace_syscalls(false); + process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! + } + + current_thread->yield_if_stopped(); + + current_thread->check_dispatch_pending_signal(); + + // If the previous mode somehow changed something is seriously messed up... + VERIFY(current_thread->previous_mode() == ExecutionMode::User); + + // Check if we're supposed to return to userspace or just die. + current_thread->die_if_needed(); + + // Crash any processes which have committed a promise violation during syscall handling. + if (result.is_error() && result.error().code() == EPROMISEVIOLATION) { + VERIFY(current_thread->is_promise_violation_pending()); + current_thread->set_promise_violation_pending(false); + process.crash(SIGABRT, {}); + } else { + VERIFY(!current_thread->is_promise_violation_pending()); + } + + VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); +} + +} -- cgit v1.2.3