Serenity Operating System
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}