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>
16#include <mos_stdlib.hpp>
17#include <mos_string.hpp>
18
19static 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
29static 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
39static 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(std::move(current_process), 0, signal);
52}
53
54[[noreturn]] static void signal_do_terminate(signal_t signal)
55{
56 if (current_thread == current_process->main_thread)
57 process_exit(std::move(current_process), 0, signal);
58 else
59 thread_exit(std::move(current_thread));
61}
62
63static void signal_do_ignore(signal_t signal)
64{
65 pr_dinfo2(signal, "thread %pt ignoring signal %d", current_thread, signal);
66}
67
68static 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
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
96
97 bool has_pending = false; // true if the signal is already pending
99 {
100 has_pending |= pending->signal == signal;
101 if (has_pending)
102 break;
103 }
104
105 if (!has_pending)
106 {
108 linked_list_init(list_node(sigdesc));
109 sigdesc->signal = signal;
110 list_node_append(&target->signal_info.pending, list_node(sigdesc));
111 }
112
114
115 if (target != current_thread)
116 scheduler_wake_thread(target);
117
118 return 0;
119}
120
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_thread, signal);
164
165 return 0;
166}
167
169{
170 signal_t signal = 0;
171
172 MOS_ASSERT(spinlock_is_locked(&current_thread->signal_info.lock));
173 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
174 {
175 if (sigset_test(&current_thread->signal_info.mask, pending->signal))
176 {
177 // if a fatal signal is pending but also masked, kill the thread
178 if (is_fatal_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(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
195static 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(next_signal))
200 goto done; // init only receives signals it wants
201
202 switch (next_signal)
203 {
204 case SIGINT: signal_do_terminate(next_signal); break;
205 case SIGILL: signal_do_coredump(next_signal); break;
206 case SIGTRAP: signal_do_coredump(next_signal); break;
207 case SIGABRT: signal_do_coredump(next_signal); break;
208 case SIGKILL: signal_do_terminate(next_signal); break;
209 case SIGSEGV: signal_do_coredump(next_signal); break;
210 case SIGTERM: signal_do_terminate(next_signal); break;
211 case SIGCHLD: signal_do_ignore(next_signal); break;
212 case SIGPIPE: signal_do_terminate(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(next_signal);
225 return;
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 platform_jump_to_signal_handler(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; // 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);
254}
255
257{
259
260 spinlock_acquire(&current_thread->signal_info.lock);
261 const signal_t next_signal = signal_get_next_pending();
262 spinlock_release(&current_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);
276 goto really_prepare;
277 }
278 // else: fall through, return -EINTR
279 }
280
281 platform_syscall_store_retval(regs, real_ret);
282
283 if (!next_signal)
284 return; // no pending signal, leave asap
285
286really_prepare:
287 do_signal_exit_to_user_prepare(regs, next_signal, &action);
288}
289
291{
292 if (!data->was_masked)
293 sigset_del(&current_thread->signal_info.mask, data->signal);
294}
295
297{
298 bool has_pending = false;
299 spinlock_acquire(&current_thread->signal_info.lock);
300 list_foreach(sigpending_t, pending, current_thread->signal_info.pending)
301 {
302 if (sigset_test(&current_thread->signal_info.mask, pending->signal))
303 continue; // signal is masked, skip it
304 has_pending = true;
305 break;
306 }
307
308 spinlock_release(&current_thread->signal_info.lock);
309 return has_pending;
310}
#define MOS_UNREACHABLE_X(msg,...)
Definition assert.hpp:12
#define MOS_ASSERT(cond)
Definition assert.hpp:14
#define MOS_UNREACHABLE()
Definition assert.hpp:11
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:121
void signal_exit_to_user_prepare_syscall(platform_regs_t *regs, reg_t syscall_nr, reg_t syscall_ret)
Prepare to exit to userspace after a syscall.
Definition signal.cpp:256
void signal_on_returned(sigreturn_data_t *data)
Return from a signal handler.
Definition signal.cpp:290
bool signal_has_pending(void)
Return true if there's a pending signal.
Definition signal.cpp:296
void signal_exit_to_user_prepare(platform_regs_t *regs)
Prepare to exit to userspace.
Definition signal.cpp:240
long signal_send_to_thread(Thread *target, signal_t signal)
Send a signal to a thread.
Definition signal.cpp:87
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:175
T * create(Args &&...args)
Definition allocator.hpp:10
#define NULL
Definition pb_syshdr.h:46
#define CHAR_BIT
Definition pb_syshdr.h:117
#define current_thread
Definition platform.hpp:32
@ THREAD_STATE_READY
thread can be scheduled
Definition platform.hpp:68
@ THREAD_STATE_RUNNING
thread is currently running
Definition platform.hpp:69
@ THREAD_STATE_BLOCKED
thread is blocked by a wait condition
Definition platform.hpp:70
@ THREAD_STATE_CREATED
created or forked, but not ever started
Definition platform.hpp:67
#define current_process
Definition platform.hpp:33
#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:284
void platform_syscall_setup_restart_context(platform_regs_t *regs, reg_t syscall_nr)
void platform_jump_to_signal_handler(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:91
mos::shared_ptr< T > ptr
static void do_signal_exit_to_user_prepare(platform_regs_t *regs, signal_t next_signal, const sigaction_t *action)
Definition signal.cpp:195
static bool is_fatal_signal(signal_t signal)
Definition signal.cpp:68
static void signal_do_coredump(signal_t signal)
Definition signal.cpp:48
static int sigset_add(sigset_t *sigset, int sig)
Definition signal.cpp:19
static void signal_do_terminate(signal_t signal)
Definition signal.cpp:54
static int sigset_del(sigset_t *sigset, int sig)
Definition signal.cpp:29
static void signal_do_ignore(signal_t signal)
Definition signal.cpp:63
static int sigset_test(const sigset_t *sigset, int sig)
Definition signal.cpp:39
static signal_t signal_get_next_pending(void)
Definition signal.cpp:168
#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:66
list_head pending
list of pending signals
uintn reg_t
Definition types.h:47