1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/tasks/signal.h"
4
5#include "mos/mm/slab.h"
6#include "mos/mm/slab_autoinit.h"
7#include "mos/platform/platform.h"
8#include "mos/syslog/printk.h"
9#include "mos/tasks/process.h"
10#include "mos/tasks/schedule.h"
11#include "mos/tasks/thread.h"
12
13#include <errno.h>
14#include <limits.h>
15#include <mos/lib/structures/list.h>
16#include <mos/lib/sync/spinlock.h>
17#include <mos/tasks/signal_types.h>
18#include <mos_stdlib.h>
19#include <mos_string.h>
20
21static int sigset_add(sigset_t *sigset, int sig)
22{
23 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
24
25 const int signo = sig - 1;
26 char *const ptr = (char *) sigset;
27 ptr[signo / CHAR_BIT] |= (1 << (signo % CHAR_BIT));
28 return 0;
29}
30
31static int sigset_del(sigset_t *sigset, int sig)
32{
33 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
34
35 const int signo = sig - 1;
36 char *const ptr = (char *) sigset;
37 ptr[signo / CHAR_BIT] &= ~(1 << (signo % CHAR_BIT));
38 return 0;
39}
40
41static int sigset_test(const sigset_t *sigset, int sig)
42{
43 MOS_ASSERT(sig >= 1 && sig <= SIGNAL_MAX_N);
44
45 const int signo = sig - 1;
46 const char *ptr = (const char *) sigset;
47 return (ptr[signo / CHAR_BIT] & (1 << (signo % CHAR_BIT))) != 0;
48}
49
50slab_t *sigpending_slab = NULL;
51SLAB_AUTOINIT("signal_pending", sigpending_slab, sigpending_t);
52
53[[noreturn]] static void signal_do_coredump(signal_t signal)
54{
55 process_exit(current_process, exit_code: 0, signal);
56 MOS_UNREACHABLE();
57}
58
59[[noreturn]] static void signal_do_terminate(signal_t signal)
60{
61 if (current_thread == current_process->main_thread)
62 process_exit(current_process, exit_code: 0, signal);
63 else
64 thread_exit(current_thread);
65 MOS_UNREACHABLE();
66}
67
68static void signal_do_ignore(signal_t signal)
69{
70 pr_dinfo2(signal, "thread %pt ignoring signal %d", (void *) current_thread, signal);
71}
72
73static bool is_fatal_signal(signal_t signal)
74{
75 switch (signal)
76 {
77 case SIGILL:
78 case SIGTRAP:
79 case SIGABRT:
80 case SIGKILL:
81 case SIGSEGV: return true;
82
83 case SIGINT:
84 case SIGTERM:
85 case SIGCHLD:
86 case SIGPIPE: return false;
87
88 default: MOS_UNREACHABLE_X("handle this signal %d", signal); break;
89 }
90}
91
92long signal_send_to_thread(thread_t *target, signal_t signal)
93{
94 if (target->mode == THREAD_MODE_KERNEL && !is_fatal_signal(signal))
95 {
96 pr_emerg("signal_send_to_thread(%pt, %d): cannot send non-fatal signal to kernel thread", (void *) target, signal);
97 return -EINVAL;
98 }
99
100 spinlock_acquire(&target->signal_info.lock);
101
102 bool has_pending = false; // true if the signal is already pending
103 list_foreach(sigpending_t, pending, target->signal_info.pending)
104 {
105 has_pending |= pending->signal == signal;
106 if (has_pending)
107 break;
108 }
109
110 if (!has_pending)
111 {
112 sigpending_t *sigdesc = kmalloc(sigpending_slab);
113 linked_list_init(list_node(sigdesc));
114 sigdesc->signal = signal;
115 list_node_append(head: &target->signal_info.pending, list_node(sigdesc));
116 }
117
118 spinlock_release(&target->signal_info.lock);
119
120 if (target != current_thread)
121 scheduler_wake_thread(thread: target);
122
123 return 0;
124}
125
126long signal_send_to_process(process_t *target, signal_t signal)
127{
128 if (target->pid == 1 && signal == SIGKILL)
129 {
130 pr_emerg("signal_send_to_process(%pp, %d): cannot send SIGKILL to init", (void *) target, signal);
131 return -EINVAL;
132 }
133
134 if (target->pid == 2)
135 {
136 pr_emerg("signal_send_to_process(%pp, %d): cannot send signal to kthreadd", (void *) target, signal);
137 return -EINVAL;
138 }
139
140 thread_t *target_thread = NULL;
141 list_foreach(thread_t, thread, target->threads)
142 {
143 if (thread->state == THREAD_STATE_RUNNING || thread->state == THREAD_STATE_READY || thread->state == THREAD_STATE_CREATED)
144 {
145 target_thread = thread;
146 break;
147 }
148 }
149
150 if (!target_thread)
151 {
152 list_foreach(thread_t, thread, target->threads)
153 {
154 if (thread->state == THREAD_STATE_BLOCKED)
155 {
156 target_thread = thread;
157 break;
158 }
159 }
160 }
161
162 if (!target_thread)
163 {
164 pr_emerg("signal_send_to_process(%pp, %d): no thread to send signal to", (void *) target, signal);
165 return -EINVAL;
166 }
167
168 signal_send_to_thread(target: target_thread, signal);
169
170 return 0;
171}
172
173static signal_t signal_get_next_pending(void)
174{
175 signal_t signal = 0;
176
177 MOS_ASSERT(spinlock_is_locked(&current_thread->signal_info.lock));
178 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
179 {
180 if (sigset_test(sigset: &current_thread->signal_info.mask, sig: pending->signal))
181 {
182 // if a fatal signal is pending but also masked, kill the thread
183 if (is_fatal_signal(signal: pending->signal))
184 {
185 pr_emerg("thread %pt received fatal signal %d but it was masked, terminating", (void *) current_thread, pending->signal);
186 signal_do_terminate(signal: pending->signal);
187 }
188 continue; // signal is masked, skip it
189 }
190
191 list_remove(pending);
192 signal = pending->signal;
193 kfree(ptr: pending);
194 break;
195 }
196
197 return signal;
198}
199
200static void do_signal_exit_to_user_prepare(platform_regs_t *regs, signal_t next_signal, const sigaction_t *action)
201{
202 if (action->handler == SIG_DFL)
203 {
204 if (current_process->pid == 1 && !is_fatal_signal(signal: next_signal))
205 goto done; // init only receives signals it wants
206
207 switch (next_signal)
208 {
209 case SIGINT: signal_do_terminate(signal: next_signal); break;
210 case SIGILL: signal_do_coredump(signal: next_signal); break;
211 case SIGTRAP: signal_do_coredump(signal: next_signal); break;
212 case SIGABRT: signal_do_coredump(signal: next_signal); break;
213 case SIGKILL: signal_do_terminate(signal: next_signal); break;
214 case SIGSEGV: signal_do_coredump(signal: next_signal); break;
215 case SIGTERM: signal_do_terminate(signal: next_signal); break;
216 case SIGCHLD: signal_do_ignore(signal: next_signal); break;
217 case SIGPIPE: signal_do_terminate(signal: next_signal); break;
218
219 default: MOS_UNREACHABLE_X("handle this signal %d", next_signal); break;
220 }
221
222 // the default handler returns
223 done:
224 return;
225 }
226
227 if (action->handler == SIG_IGN)
228 {
229 signal_do_ignore(signal: next_signal);
230 return;
231 }
232
233 const bool was_masked = sigset_test(sigset: &current_thread->signal_info.mask, sig: next_signal);
234 if (!was_masked)
235 sigset_add(sigset: &current_thread->signal_info.mask, sig: next_signal);
236
237 const sigreturn_data_t data = {
238 .signal = next_signal,
239 .was_masked = was_masked,
240 };
241
242 platform_jump_to_signal_handler(regs, sigreturn_data: &data, sa: action); // save previous register states onto user stack
243}
244
245void signal_exit_to_user_prepare(platform_regs_t *regs)
246{
247 MOS_ASSERT(current_thread);
248
249 spinlock_acquire(&current_thread->signal_info.lock);
250 const signal_t next_signal = signal_get_next_pending();
251 spinlock_release(&current_thread->signal_info.lock);
252
253 if (!next_signal)
254 return; // no pending signal, leave asap
255
256 const sigaction_t action = current_process->signal_info.handlers[next_signal];
257
258 do_signal_exit_to_user_prepare(regs, next_signal, action: &action);
259}
260
261void signal_exit_to_user_prepare_syscall(platform_regs_t *regs, reg_t syscall_nr, reg_t syscall_ret)
262{
263 MOS_ASSERT(current_thread);
264
265 spinlock_acquire(&current_thread->signal_info.lock);
266 const signal_t next_signal = signal_get_next_pending();
267 spinlock_release(&current_thread->signal_info.lock);
268
269 const sigaction_t action = current_process->signal_info.handlers[next_signal];
270
271 reg_t real_ret = syscall_ret;
272 if (syscall_ret == (reg_t) -ERESTARTSYS)
273 {
274 MOS_ASSERT(next_signal);
275 real_ret = -EINTR;
276
277 if (action.sa_flags & SA_RESTART)
278 {
279 pr_dinfo2(signal, "thread %pt will restart syscall %lu after signal %d", (void *) current_thread, syscall_nr, next_signal);
280 platform_syscall_setup_restart_context(regs, syscall_nr);
281 goto really_prepare;
282 }
283 // else: fall through, return -EINTR
284 }
285
286 platform_syscall_store_retval(regs, result: real_ret);
287
288 if (!next_signal)
289 return; // no pending signal, leave asap
290
291really_prepare:
292 do_signal_exit_to_user_prepare(regs, next_signal, action: &action);
293}
294
295void signal_on_returned(sigreturn_data_t *data)
296{
297 if (!data->was_masked)
298 sigset_del(sigset: &current_thread->signal_info.mask, sig: data->signal);
299}
300
301bool signal_has_pending(void)
302{
303 bool has_pending = false;
304 spinlock_acquire(&current_thread->signal_info.lock);
305 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
306 {
307 if (sigset_test(sigset: &current_thread->signal_info.mask, sig: pending->signal))
308 continue; // signal is masked, skip it
309 has_pending = true;
310 break;
311 }
312
313 spinlock_release(&current_thread->signal_info.lock);
314 return has_pending;
315}
316