MOS Source Code
Loading...
Searching...
No Matches
signal.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
4
10
11#include <errno.h>
12#include <limits.h>
15#include <mos/syscall/number.h>
17#include <mos_stdlib.hpp>
18#include <mos_string.hpp>
19
20static int sigset_add(sigset_t *sigset, int sig)
21{
22 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
23
24 const int signo = sig - 1;
25 char *const ptr = (char *) sigset;
26 ptr[signo / CHAR_BIT] |= (1 << (signo % CHAR_BIT));
27 return 0;
28}
29
30static int sigset_del(sigset_t *sigset, int sig)
31{
32 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
33
34 const int signo = sig - 1;
35 char *const ptr = (char *) sigset;
36 ptr[signo / CHAR_BIT] &= ~(1 << (signo % CHAR_BIT));
37 return 0;
38}
39
40static int sigset_test(const sigset_t *sigset, int sig)
41{
42 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
43
44 const int signo = sig - 1;
45 const char *ptr = (const char *) sigset;
46 return (ptr[signo / CHAR_BIT] & (1 << (signo % CHAR_BIT))) != 0;
47}
48
49[[noreturn]] static void signal_do_coredump(signal_t signal)
50{
51 process_exit(std::move(current_process), 0, signal);
53}
54
55[[noreturn]] static void signal_do_terminate(signal_t signal)
56{
57 if (current_thread == current_process->main_thread)
58 process_exit(std::move(current_process), 0, signal);
59 else
60 thread_exit(std::move(current_thread));
62}
63
64static void signal_do_ignore(signal_t signal)
65{
66 pr_dinfo2(signal, "thread %pt ignoring signal %d", current_thread, signal);
67}
68
69static bool is_fatal_signal(signal_t signal)
70{
71 switch (signal)
72 {
73 case SIGILL:
74 case SIGTRAP:
75 case SIGABRT:
76 case SIGKILL:
77 case SIGSEGV: return true;
78
79 case SIGINT:
80 case SIGTERM:
81 case SIGCHLD:
82 case SIGPIPE: return false;
83
84 default: pr_emerg("is_fatal_signal(%d): returning true", signal); return true;
85 }
86}
87
89{
90 if (target->mode == THREAD_MODE_KERNEL && !is_fatal_signal(signal))
91 {
92 pr_emerg("signal_send_to_thread(%pt, %d): cannot send non-fatal signal to kernel thread", target, signal);
93 return -EINVAL;
94 }
95
97
98 bool has_pending = false; // true if the signal is already pending
100 {
101 has_pending |= pending->signal == signal;
102 if (has_pending)
103 break;
104 }
105
106 if (!has_pending)
107 {
109 linked_list_init(list_node(sigdesc));
110 sigdesc->signal = signal;
111 list_node_append(&target->signal_info.pending, list_node(sigdesc));
112 }
113
115
116 if (target != current_thread)
117 scheduler_wake_thread(target);
118
119 return 0;
120}
121
123{
124 if (target->pid == 1 && signal == SIGKILL)
125 {
126 pr_emerg("signal_send_to_process(%pp, %d): cannot send SIGKILL to init", target, signal);
127 return -EINVAL;
128 }
129
130 if (target->pid == 2)
131 {
132 pr_emerg("signal_send_to_process(%pp, %d): cannot send signal to kthreadd", target, signal);
133 return -EINVAL;
134 }
135
136 Thread *target_thread = NULL;
137 for (const auto &thread : target->thread_list)
138 {
139 if (thread->state == THREAD_STATE_RUNNING || thread->state == THREAD_STATE_READY || thread->state == THREAD_STATE_CREATED)
140 {
141 target_thread = thread;
142 break;
143 }
144 }
145
146 if (!target_thread)
147 {
148 for (const auto &thread : target->thread_list)
149 {
150 if (thread->state == THREAD_STATE_BLOCKED)
151 {
152 target_thread = thread;
153 break;
154 }
155 }
156 }
157
158 if (!target_thread)
159 {
160 pr_emerg("signal_send_to_process(%pp, %d): no thread to send signal to", target, signal);
161 return -EINVAL;
162 }
163
164 signal_send_to_thread(target_thread, signal);
165
166 return 0;
167}
168
170{
171 signal_t signal = 0;
172
173 MOS_ASSERT(spinlock_is_locked(&current_thread->signal_info.lock));
174 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
175 {
176 if (sigset_test(&current_thread->signal_info.mask, pending->signal))
177 {
178 // if a fatal signal is pending but also masked, kill the thread
179 if (is_fatal_signal(pending->signal))
180 {
181 pr_emerg("thread %pt received fatal signal %d but it was masked, terminating", current_thread, pending->signal);
182 signal_do_terminate(pending->signal);
183 }
184 continue; // signal is masked, skip it
185 }
186
187 list_remove(pending);
188 signal = pending->signal;
189 delete pending;
190 break;
191 }
192
193 return signal;
194}
195
197{
198 if (action->handler == SIG_DFL)
199 {
200 if (current_process->pid == 1 && !is_fatal_signal(next_signal))
201 goto done; // init only receives signals it wants
202
203 switch (next_signal)
204 {
205 case SIGINT: signal_do_terminate(next_signal); break;
206 case SIGILL: signal_do_coredump(next_signal); break;
207 case SIGTRAP: signal_do_coredump(next_signal); break;
208 case SIGABRT: signal_do_coredump(next_signal); break;
209 case SIGKILL: signal_do_terminate(next_signal); break;
210 case SIGSEGV: signal_do_coredump(next_signal); break;
211 case SIGPIPE: signal_do_terminate(next_signal); break;
212 case SIGTERM: signal_do_terminate(next_signal); break;
213 case SIGCHLD: signal_do_ignore(next_signal); break;
214 default: pr_emerg("unknown signal %d, skipping", next_signal); break;
215 }
216
217 // the default handler returns
218 done:
219 return nullptr;
220 }
221
222 if (action->handler == SIG_IGN)
223 {
224 signal_do_ignore(next_signal);
225 return nullptr;
226 }
227
228 const bool was_masked = sigset_test(&current_thread->signal_info.mask, next_signal);
229 if (!was_masked)
230 sigset_add(&current_thread->signal_info.mask, next_signal);
231
232 const sigreturn_data_t data = {
233 .signal = next_signal,
234 .was_masked = was_masked,
235 };
236
237 return platform_setup_signal_handler_regs(regs, &data, action); // save previous register states onto user stack
238}
239
241{
243
244 spinlock_acquire(&current_thread->signal_info.lock);
245 const signal_t next_signal = signal_get_next_pending();
246 spinlock_release(&current_thread->signal_info.lock);
247
248 if (!next_signal)
249 return nullptr; // no pending signal, leave asap
250
251 const sigaction_t action = current_process->signal_info.handlers[next_signal];
252
253 pr_dinfo2(signal, "thread %pt will handle signal %d with handler %p", current_thread, next_signal, action.handler);
254 return do_signal_exit_to_user_prepare(regs, next_signal, &action);
255}
256
258{
260
261 spinlock_acquire(&current_thread->signal_info.lock);
262 const signal_t next_signal = signal_get_next_pending();
263 spinlock_release(&current_thread->signal_info.lock);
264
265 const sigaction_t action = current_process->signal_info.handlers[next_signal];
266
267 // HACK: return if new thread (platform_setup_thread clears a7)
268 if (syscall_nr == 0 && syscall_ret == 0)
269 return nullptr;
270
271 if (syscall_ret == (reg_t) -ERESTARTSYS)
272 {
273 MOS_ASSERT(next_signal);
274
275 if (action.sa_flags & SA_RESTART)
276 {
277 pr_dinfo2(signal, "thread %pt will restart syscall %lu after signal %d", current_thread, syscall_nr, next_signal);
279 goto real_prepare;
280 }
281 else
282 {
283 platform_syscall_store_retval(regs, -EINTR);
284 }
285 }
286 else
287 {
288 platform_syscall_store_retval(regs, syscall_ret);
289 }
290
291 if (!next_signal)
292 return nullptr; // no pending signal, leave asap
293
294real_prepare:
295 pr_dinfo2(signal, "thread %pt will handle signal %d with handler %p when returning from syscall", current_thread, next_signal, action.handler);
296 return do_signal_exit_to_user_prepare(regs, next_signal, &action);
297}
298
300{
301 if (!data->was_masked)
302 sigset_del(&current_thread->signal_info.mask, data->signal);
303
304 pr_dinfo2(signal, "thread %pt returned from signal %d", current_thread, data->signal);
305}
306
308{
309 bool has_pending = false;
310 spinlock_acquire(&current_thread->signal_info.lock);
311 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
312 {
313 if (sigset_test(&current_thread->signal_info.mask, pending->signal))
314 continue; // signal is masked, skip it
315 has_pending = true;
316 break;
317 }
318
319 spinlock_release(&current_thread->signal_info.lock);
320 return has_pending;
321}
#define MOS_ASSERT(cond)
Definition assert.hpp:19
#define MOS_UNREACHABLE()
Definition assert.hpp:10
long signal_send_to_process(Process *target, signal_t signal)
Send a signal to a process, an arbitrary thread will be chosen to receive the signal.
Definition signal.cpp:122
void signal_on_returned(sigreturn_data_t *data)
Return from a signal handler.
Definition signal.cpp:299
ptr< platform_regs_t > signal_exit_to_user_prepare(platform_regs_t *regs)
Prepare to exit to userspace.
Definition signal.cpp:240
bool signal_has_pending(void)
Return true if there's a pending signal.
Definition signal.cpp:307
long signal_send_to_thread(Thread *target, signal_t signal)
Send a signal to a thread.
Definition signal.cpp:88
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.cpp:15
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.cpp:68
#define list_foreach(t, v, h)
Iterate over a list.
Definition list.hpp:89
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.hpp:74
#define list_remove(element)
Definition list.hpp:80
@ THREAD_MODE_KERNEL
void define_syscall thread_exit(void)
Definition ksyscall.cpp:193
T * create(Args &&...args)
Definition allocator.hpp:12
#define NULL
Definition pb_syshdr.h:46
#define CHAR_BIT
Definition pb_syshdr.h:117
#define current_thread
Definition platform.hpp:33
@ THREAD_STATE_READY
thread can be scheduled
Definition platform.hpp:45
@ THREAD_STATE_RUNNING
thread is currently running
Definition platform.hpp:46
@ THREAD_STATE_BLOCKED
thread is blocked by a wait condition
Definition platform.hpp:47
@ THREAD_STATE_CREATED
created or forked, but not ever started
Definition platform.hpp:44
#define current_process
Definition platform.hpp:34
#define pr_emerg(fmt,...)
Definition printk.hpp:39
#define pr_dinfo2(feat, fmt,...)
Definition printk.hpp:27
void process_exit(Process *&&proc, u8 exit_code, signal_t signal)
Definition process.cpp:289
void platform_syscall_setup_restart_context(platform_regs_t *regs, reg_t syscall_nr)
ptr< platform_regs_t > platform_setup_signal_handler_regs(const platform_regs_t *regs, const sigreturn_data_t *sigreturn_data, const sigaction_t *sa)
void platform_syscall_store_retval(platform_regs_t *regs, reg_t result)
void scheduler_wake_thread(Thread *thread)
Wake a thread.
Definition schedule.cpp:90
mos::shared_ptr< T > ptr
static bool is_fatal_signal(signal_t signal)
Definition signal.cpp:69
static void signal_do_coredump(signal_t signal)
Definition signal.cpp:49
static int sigset_add(sigset_t *sigset, int sig)
Definition signal.cpp:20
static void signal_do_terminate(signal_t signal)
Definition signal.cpp:55
static int sigset_del(sigset_t *sigset, int sig)
Definition signal.cpp:30
static void signal_do_ignore(signal_t signal)
Definition signal.cpp:64
static int sigset_test(const sigset_t *sigset, int sig)
Definition signal.cpp:40
static ptr< platform_regs_t > do_signal_exit_to_user_prepare(platform_regs_t *regs, signal_t next_signal, const sigaction_t *action)
Definition signal.cpp:196
static signal_t signal_get_next_pending(void)
Definition signal.cpp:169
#define ERESTARTSYS
Definition signal.hpp:11
int signal_t
#define SIGNAL_MAX_N
should_inline bool spinlock_is_locked(const spinlock_t *lock)
Definition spinlock.hpp:71
#define spinlock_acquire(lock)
Definition spinlock.hpp:64
#define spinlock_release(lock)
Definition spinlock.hpp:65
mos::list< Thread * > thread_list
pid_t pid
thread_mode mode
user-mode thread or kernel-mode
thread_signal_info_t signal_info
unsigned long sa_flags
__sighandler handler
A pending signal.
Definition signal.hpp:25
signal_t signal
Definition signal.hpp:27
signal_t signal
Definition signal.hpp:68
list_head pending
list of pending signals
uintn reg_t
Definition types.h:47