Serenity Operating System
at master 194 lines 6.4 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <Kernel/API/Syscall.h> 9#include <Kernel/Arch/RegisterState.h> 10#include <Kernel/Arch/TrapFrame.h> 11#include <Kernel/Memory/MemoryManager.h> 12#include <Kernel/Panic.h> 13#include <Kernel/PerformanceManager.h> 14#include <Kernel/Process.h> 15#include <Kernel/Scheduler.h> 16#include <Kernel/Sections.h> 17#include <Kernel/ThreadTracer.h> 18 19namespace Kernel { 20 21namespace Syscall { 22 23using Handler = auto(Process::*)(FlatPtr, FlatPtr, FlatPtr, FlatPtr) -> ErrorOr<FlatPtr>; 24using HandlerWithRegisterState = auto(Process::*)(RegisterState&) -> ErrorOr<FlatPtr>; 25 26struct HandlerMetadata { 27 Handler handler; 28 NeedsBigProcessLock needs_lock; 29}; 30 31#define __ENUMERATE_SYSCALL(sys_call, needs_lock) { bit_cast<Handler>(&Process::sys$##sys_call), needs_lock }, 32static const HandlerMetadata s_syscall_table[] = { 33 ENUMERATE_SYSCALLS(__ENUMERATE_SYSCALL) 34}; 35#undef __ENUMERATE_SYSCALL 36 37ErrorOr<FlatPtr> handle(RegisterState& regs, FlatPtr function, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3, FlatPtr arg4) 38{ 39 VERIFY_INTERRUPTS_ENABLED(); 40 auto* current_thread = Thread::current(); 41 auto& process = current_thread->process(); 42 current_thread->did_syscall(); 43 44 PerformanceManager::add_syscall_event(*current_thread, regs); 45 46 if (function >= Function::__Count) { 47 dbgln("Unknown syscall {} requested ({:p}, {:p}, {:p}, {:p})", function, arg1, arg2, arg3, arg4); 48 return ENOSYS; 49 } 50 51 auto const syscall_metadata = s_syscall_table[function]; 52 if (syscall_metadata.handler == nullptr) { 53 dbgln("Null syscall {} requested, you probably need to rebuild this program!", function); 54 return ENOSYS; 55 } 56 57 MutexLocker mutex_locker; 58 auto const needs_big_lock = syscall_metadata.needs_lock == NeedsBigProcessLock::Yes; 59 if (needs_big_lock) { 60 mutex_locker.attach_and_lock(process.big_lock()); 61 }; 62 63 if (function == SC_exit || function == SC_exit_thread) { 64 // These syscalls need special handling since they never return to the caller. 65 // In these cases the process big lock will get released on the exit of the thread. 66 67 if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { 68 regs.set_return_reg(0); 69 tracer->set_trace_syscalls(false); 70 process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! 71 } 72 73 switch (function) { 74 case SC_exit: 75 process.sys$exit(arg1); 76 case SC_exit_thread: 77 process.sys$exit_thread(arg1, arg2, arg3); 78 default: 79 VERIFY_NOT_REACHED(); 80 } 81 } 82 83 ErrorOr<FlatPtr> result { FlatPtr(nullptr) }; 84 if (function == SC_fork || function == SC_sigreturn) { 85 // These syscalls want the RegisterState& rather than individual parameters. 86 auto handler = bit_cast<HandlerWithRegisterState>(syscall_metadata.handler); 87 result = (process.*(handler))(regs); 88 } else { 89 result = (process.*(syscall_metadata.handler))(arg1, arg2, arg3, arg4); 90 } 91 92 return result; 93} 94 95} 96 97extern "C" NEVER_INLINE void syscall_handler(TrapFrame* trap); 98NEVER_INLINE void syscall_handler(TrapFrame* trap) 99{ 100#if ARCH(X86_64) 101 // Make sure SMAP protection is enabled on syscall entry. 102 clac(); 103#elif ARCH(AARCH64) 104 // FIXME: Implement the security mechanism for aarch64 105#else 106# error Unknown architecture 107#endif 108 109 auto& regs = *trap->regs; 110 auto* current_thread = Thread::current(); 111 VERIFY(current_thread->previous_mode() == ExecutionMode::User); 112 auto& process = current_thread->process(); 113 if (process.is_dying()) { 114 // It's possible this thread is just about to make a syscall while another is 115 // is killing our process. 116 current_thread->die_if_needed(); 117 return; 118 } 119 120 if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { 121 tracer->set_trace_syscalls(false); 122 process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! 123 } 124 125 current_thread->yield_if_stopped(); 126 127#if ARCH(X86_64) 128 // Apply a random offset in the range 0-255 to the stack pointer, 129 // to make kernel stacks a bit less deterministic. 130 u32 lsw; 131 u32 msw; 132 read_tsc(lsw, msw); 133 134 auto* ptr = (char*)__builtin_alloca(lsw & 0xff); 135 asm volatile("" 136 : "=m"(*ptr)); 137 138 constexpr FlatPtr iopl_mask = 3u << 12; 139 140 FlatPtr flags = regs.flags(); 141 if ((flags & (iopl_mask)) != 0) { 142 PANIC("Syscall from process with IOPL != 0"); 143 } 144#elif ARCH(AARCH64) 145 // FIXME: Implement the security mechanism for aarch64 146#else 147# error Unknown architecture 148#endif 149 150 Memory::MemoryManager::validate_syscall_preconditions(process, regs); 151 152 FlatPtr function; 153 FlatPtr arg1; 154 FlatPtr arg2; 155 FlatPtr arg3; 156 FlatPtr arg4; 157 regs.capture_syscall_params(function, arg1, arg2, arg3, arg4); 158 159 auto result = Syscall::handle(regs, function, arg1, arg2, arg3, arg4); 160 161 if (result.is_error()) { 162 regs.set_return_reg(-result.error().code()); 163 } else { 164 regs.set_return_reg(result.value()); 165 } 166 167 if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) { 168 tracer->set_trace_syscalls(false); 169 process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread! 170 } 171 172 current_thread->yield_if_stopped(); 173 174 current_thread->check_dispatch_pending_signal(); 175 176 // If the previous mode somehow changed something is seriously messed up... 177 VERIFY(current_thread->previous_mode() == ExecutionMode::User); 178 179 // Check if we're supposed to return to userspace or just die. 180 current_thread->die_if_needed(); 181 182 // Crash any processes which have committed a promise violation during syscall handling. 183 if (result.is_error() && result.error().code() == EPROMISEVIOLATION) { 184 VERIFY(current_thread->is_promise_violation_pending()); 185 current_thread->set_promise_violation_pending(false); 186 process.crash(SIGABRT, {}); 187 } else { 188 VERIFY(!current_thread->is_promise_violation_pending()); 189 } 190 191 VERIFY(!g_scheduler_lock.is_locked_by_current_processor()); 192} 193 194}