Serenity Operating System
1/*
2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Checked.h>
8#include <Kernel/Memory/MemoryManager.h>
9#include <Kernel/PerformanceManager.h>
10#include <Kernel/Process.h>
11#include <Kernel/Scheduler.h>
12
13namespace Kernel {
14
15ErrorOr<FlatPtr> Process::sys$create_thread(void* (*entry)(void*), Userspace<Syscall::SC_create_thread_params const*> user_params)
16{
17 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
18 TRY(require_promise(Pledge::thread));
19 auto params = TRY(copy_typed_from_user(user_params));
20
21 unsigned detach_state = params.detach_state;
22 int schedule_priority = params.schedule_priority;
23 unsigned stack_size = params.stack_size;
24
25 auto user_sp = Checked<FlatPtr>((FlatPtr)params.stack_location);
26 user_sp += stack_size;
27 if (user_sp.has_overflow())
28 return EOVERFLOW;
29
30 TRY(address_space().with([&](auto& space) -> ErrorOr<void> {
31 if (!MM.validate_user_stack(*space, VirtualAddress(user_sp.value() - 4)))
32 return EFAULT;
33 return {};
34 }));
35
36 // FIXME: return EAGAIN if Thread::all_threads().size() is greater than PTHREAD_THREADS_MAX
37
38 int requested_thread_priority = schedule_priority;
39 if (requested_thread_priority < THREAD_PRIORITY_MIN || requested_thread_priority > THREAD_PRIORITY_MAX)
40 return EINVAL;
41
42 bool is_thread_joinable = (0 == detach_state);
43
44 // FIXME: Do something with guard pages?
45
46 auto thread = TRY(Thread::try_create(*this));
47
48 // We know this thread is not the main_thread,
49 // So give it a unique name until the user calls $set_thread_name on it
50 auto new_thread_name = TRY(name().with([&](auto& process_name) {
51 return KString::formatted("{} [{}]", process_name->view(), thread->tid().value());
52 }));
53 thread->set_name(move(new_thread_name));
54
55 if (!is_thread_joinable)
56 thread->detach();
57
58 auto& regs = thread->regs();
59 regs.set_ip((FlatPtr)entry);
60 regs.set_sp(user_sp.value());
61
62#if ARCH(X86_64)
63 regs.set_flags(0x0202);
64 regs.cr3 = address_space().with([](auto& space) { return space->page_directory().cr3(); });
65
66 regs.rdi = params.rdi;
67 regs.rsi = params.rsi;
68 regs.rdx = params.rdx;
69 regs.rcx = params.rcx;
70#endif
71
72 TRY(thread->make_thread_specific_region({}));
73
74 PerformanceManager::add_thread_created_event(*thread);
75
76 SpinlockLocker lock(g_scheduler_lock);
77 thread->set_priority(requested_thread_priority);
78 thread->set_state(Thread::State::Runnable);
79 return thread->tid().value();
80}
81
82void Process::sys$exit_thread(Userspace<void*> exit_value, Userspace<void*> stack_location, size_t stack_size)
83{
84 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
85
86 auto result = require_promise(Pledge::thread);
87 if (result.is_error()) {
88 // Crash now, as we will never reach back to the syscall handler.
89 crash(SIGABRT, {});
90 }
91
92 if (this->thread_count() == 1) {
93 // If this is the last thread, instead kill the process.
94 this->sys$exit(0);
95 }
96
97 auto* current_thread = Thread::current();
98 current_thread->set_profiling_suppressed();
99 PerformanceManager::add_thread_exit_event(*current_thread);
100
101 if (stack_location) {
102 auto unmap_result = address_space().with([&](auto& space) {
103 return space->unmap_mmap_range(stack_location.vaddr(), stack_size);
104 });
105 if (unmap_result.is_error())
106 dbgln("Failed to unmap thread stack, terminating thread anyway. Error code: {}", unmap_result.error());
107 }
108
109 current_thread->exit(reinterpret_cast<void*>(exit_value.ptr()));
110 VERIFY_NOT_REACHED();
111}
112
113ErrorOr<FlatPtr> Process::sys$detach_thread(pid_t tid)
114{
115 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
116 TRY(require_promise(Pledge::thread));
117 auto thread = Thread::from_tid(tid);
118 if (!thread || thread->pid() != pid())
119 return ESRCH;
120
121 if (!thread->is_joinable())
122 return EINVAL;
123
124 thread->detach();
125 return 0;
126}
127
128ErrorOr<FlatPtr> Process::sys$join_thread(pid_t tid, Userspace<void**> exit_value)
129{
130 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
131 TRY(require_promise(Pledge::thread));
132
133 auto thread = Thread::from_tid(tid);
134 if (!thread || thread->pid() != pid())
135 return ESRCH;
136
137 auto* current_thread = Thread::current();
138 if (thread == current_thread)
139 return EDEADLK;
140
141 void* joinee_exit_value = nullptr;
142
143 // NOTE: pthread_join() cannot be interrupted by signals. Only by death.
144 for (;;) {
145 ErrorOr<void> try_join_result;
146 auto result = current_thread->block<Thread::JoinBlocker>({}, *thread, try_join_result, joinee_exit_value);
147 if (result == Thread::BlockResult::NotBlocked) {
148 if (try_join_result.is_error())
149 return try_join_result.release_error();
150 break;
151 }
152 if (result == Thread::BlockResult::InterruptedByDeath)
153 break;
154 dbgln("join_thread: retrying");
155 }
156
157 if (exit_value)
158 TRY(copy_to_user(exit_value, &joinee_exit_value));
159
160 return 0;
161}
162
163ErrorOr<FlatPtr> Process::sys$kill_thread(pid_t tid, int signal)
164{
165 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
166 TRY(require_promise(Pledge::thread));
167
168 if (signal < 0 || signal >= NSIG)
169 return EINVAL;
170
171 auto thread = Thread::from_tid(tid);
172 if (!thread || thread->pid() != pid())
173 return ESRCH;
174
175 if (signal != 0)
176 thread->send_signal(signal, &Process::current());
177
178 return 0;
179}
180
181ErrorOr<FlatPtr> Process::sys$set_thread_name(pid_t tid, Userspace<char const*> user_name, size_t user_name_length)
182{
183 VERIFY_NO_PROCESS_BIG_LOCK(this);
184 TRY(require_promise(Pledge::stdio));
185
186 auto name = TRY(try_copy_kstring_from_user(user_name, user_name_length));
187
188 const size_t max_thread_name_size = 64;
189 if (name->length() > max_thread_name_size)
190 return ENAMETOOLONG;
191
192 auto thread = Thread::from_tid(tid);
193 if (!thread || thread->pid() != pid())
194 return ESRCH;
195
196 thread->set_name(move(name));
197 return 0;
198}
199
200ErrorOr<FlatPtr> Process::sys$get_thread_name(pid_t tid, Userspace<char*> buffer, size_t buffer_size)
201{
202 VERIFY_NO_PROCESS_BIG_LOCK(this);
203 TRY(require_promise(Pledge::thread));
204 if (buffer_size == 0)
205 return EINVAL;
206
207 auto thread = Thread::from_tid(tid);
208 if (!thread || thread->pid() != pid())
209 return ESRCH;
210
211 TRY(thread->name().with([&](auto& thread_name) -> ErrorOr<void> {
212 if (thread_name->view().is_null()) {
213 char null_terminator = '\0';
214 TRY(copy_to_user(buffer, &null_terminator, sizeof(null_terminator)));
215 return {};
216 }
217
218 if (thread_name->length() + 1 > buffer_size)
219 return ENAMETOOLONG;
220
221 return copy_to_user(buffer, thread_name->characters(), thread_name->length() + 1);
222 }));
223
224 return 0;
225}
226
227ErrorOr<FlatPtr> Process::sys$gettid()
228{
229 VERIFY_NO_PROCESS_BIG_LOCK(this);
230 TRY(require_promise(Pledge::stdio));
231 return Thread::current()->tid().value();
232}
233
234}