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