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 | |
21 | static 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 | |
31 | static 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 | |
41 | static 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 | |
50 | slab_t *sigpending_slab = NULL; |
51 | SLAB_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 | |
68 | static void signal_do_ignore(signal_t signal) |
69 | { |
70 | pr_dinfo2(signal, "thread %pt ignoring signal %d" , (void *) current_thread, signal); |
71 | } |
72 | |
73 | static 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 | |
92 | long 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 | |
126 | long 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 | |
173 | static signal_t signal_get_next_pending(void) |
174 | { |
175 | signal_t signal = 0; |
176 | |
177 | MOS_ASSERT(spinlock_is_locked(¤t_thread->signal_info.lock)); |
178 | list_foreach(sigpending_t, pending, current_thread->signal_info.pending) |
179 | { |
180 | if (sigset_test(sigset: ¤t_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 | |
200 | static 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: ¤t_thread->signal_info.mask, sig: next_signal); |
234 | if (!was_masked) |
235 | sigset_add(sigset: ¤t_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 | |
245 | void signal_exit_to_user_prepare(platform_regs_t *regs) |
246 | { |
247 | MOS_ASSERT(current_thread); |
248 | |
249 | spinlock_acquire(¤t_thread->signal_info.lock); |
250 | const signal_t next_signal = signal_get_next_pending(); |
251 | spinlock_release(¤t_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 | |
261 | void 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(¤t_thread->signal_info.lock); |
266 | const signal_t next_signal = signal_get_next_pending(); |
267 | spinlock_release(¤t_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 | |
291 | really_prepare: |
292 | do_signal_exit_to_user_prepare(regs, next_signal, action: &action); |
293 | } |
294 | |
295 | void signal_on_returned(sigreturn_data_t *data) |
296 | { |
297 | if (!data->was_masked) |
298 | sigset_del(sigset: ¤t_thread->signal_info.mask, sig: data->signal); |
299 | } |
300 | |
301 | bool signal_has_pending(void) |
302 | { |
303 | bool has_pending = false; |
304 | spinlock_acquire(¤t_thread->signal_info.lock); |
305 | list_foreach(sigpending_t, pending, current_thread->signal_info.pending) |
306 | { |
307 | if (sigset_test(sigset: ¤t_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(¤t_thread->signal_info.lock); |
314 | return has_pending; |
315 | } |
316 | |