1use linux_api::errno::Errno;
2use linux_api::signal::{
3 LinuxDefaultAction, SigAltStackFlags, SigProcMaskAction, Signal, SignalHandler, defaultaction,
4 siginfo_t,
5};
6use shadow_shim_helper_rs::explicit_drop::{ExplicitDrop, ExplicitDropper};
7use shadow_shim_helper_rs::syscall_types::ForeignPtr;
89use crate::host::process::Process;
10use crate::host::syscall::handler::{SyscallContext, SyscallHandler, ThreadContext};
11use crate::host::thread::Thread;
1213impl SyscallHandler {
14log_syscall!(
15 kill,
16/* rv */ std::ffi::c_int,
17/* pid */ linux_api::posix_types::kernel_pid_t,
18/* sig */ std::ffi::c_int,
19 );
20pub fn kill(
21 ctx: &mut SyscallContext,
22 pid: linux_api::posix_types::kernel_pid_t,
23 sig: std::ffi::c_int,
24 ) -> Result<(), Errno> {
25log::trace!("kill called on pid {pid} with signal {sig}");
2627let pid = if pid == -1 {
28// kill(2): If pid equals -1, then sig is sent to every process for which the calling
29 // process has permission to send signals, except for process 1.
30 //
31 // Currently unimplemented, and unlikely to be needed in the context of a shadow
32 // simulation.
33log::warn!("kill with pid=-1 unimplemented");
34return Err(Errno::ENOTSUP);
35 } else if pid == 0 {
36// kill(2): If pid equals 0, then sig is sent to every process in the process group of
37 // the calling process.
38 //
39 // Currently every emulated process is in its own process group.
40 //
41 // FIXME: The above comment is no longer true since implementing fork(). See
42 // https://github.com/shadow/shadow/issues/3315
43ctx.objs.process.id()
44 } else if pid < -1 {
45// kill(2): If pid is less than -1, then sig is sent to every process in the process
46 // group whose ID is -pid.
47 //
48 // Currently every emulated process is in its own process group, where pgid=pid.
49 //
50 // FIXME: The above comment is no longer true since implementing fork(). See
51 // https://github.com/shadow/shadow/issues/3315
52(-pid).try_into().or(Err(Errno::ESRCH))?
53} else {
54 pid.try_into().or(Err(Errno::ESRCH))?
55};
5657let Some(target_process) = ctx.objs.host.process_borrow(pid) else {
58log::debug!("Process {pid} not found");
59return Err(Errno::ESRCH);
60 };
61let target_process = &*target_process.borrow(ctx.objs.host.root());
6263Self::signal_process(ctx.objs, target_process, sig)
64 }
6566/// Send a signal to `target_process` from the thread and process in `objs`. A signal of 0 will
67 /// be ignored.
68fn signal_process(
69 objs: &ThreadContext,
70 target_process: &Process,
71 signal: std::ffi::c_int,
72 ) -> Result<(), Errno> {
73if signal == 0 {
74return Ok(());
75 }
7677let Ok(signal) = Signal::try_from(signal) else {
78return Err(Errno::EINVAL);
79 };
8081if signal.is_realtime() {
82log::warn!("Unimplemented signal {signal:?}");
83return Err(Errno::ENOTSUP);
84 }
8586let sender_pid = objs.process.id().into();
87let siginfo = siginfo_t::new_for_kill(signal, sender_pid, 0);
8889 target_process.signal(objs.host, Some(objs.thread), &siginfo);
9091Ok(())
92 }
9394log_syscall!(
95 tkill,
96/* rv */ std::ffi::c_int,
97/* pid */ linux_api::posix_types::kernel_pid_t,
98/* sig */ std::ffi::c_int,
99 );
100pub fn tkill(
101 ctx: &mut SyscallContext,
102 tid: linux_api::posix_types::kernel_pid_t,
103 sig: std::ffi::c_int,
104 ) -> Result<(), Errno> {
105log::trace!("tkill called on tid {tid} with signal {sig}");
106107let tid = tid.try_into().or(Err(Errno::ESRCH))?;
108109let Some(target_thread) = ctx.objs.host.thread_cloned_rc(tid) else {
110return Err(Errno::ESRCH);
111 };
112let target_thread = ExplicitDropper::new(target_thread, |value| {
113 value.explicit_drop(ctx.objs.host.root())
114 });
115let target_thread = &*target_thread.borrow(ctx.objs.host.root());
116117Self::signal_thread(ctx.objs, target_thread, sig)
118 }
119120log_syscall!(
121 tgkill,
122/* rv */ std::ffi::c_int,
123/* tgid */ linux_api::posix_types::kernel_pid_t,
124/* pid */ linux_api::posix_types::kernel_pid_t,
125/* sig */ std::ffi::c_int,
126 );
127pub fn tgkill(
128 ctx: &mut SyscallContext,
129 tgid: linux_api::posix_types::kernel_pid_t,
130 tid: linux_api::posix_types::kernel_pid_t,
131 sig: std::ffi::c_int,
132 ) -> Result<(), Errno> {
133log::trace!("tgkill called on tgid {tgid} and tid {tid} with signal {sig}");
134135let tgid = tgid.try_into().or(Err(Errno::ESRCH))?;
136let tid = tid.try_into().or(Err(Errno::ESRCH))?;
137138let Some(target_thread) = ctx.objs.host.thread_cloned_rc(tid) else {
139return Err(Errno::ESRCH);
140 };
141let target_thread = ExplicitDropper::new(target_thread, |value| {
142 value.explicit_drop(ctx.objs.host.root())
143 });
144let target_thread = &*target_thread.borrow(ctx.objs.host.root());
145146if target_thread.process_id() != tgid {
147return Err(Errno::ESRCH);
148 }
149150Self::signal_thread(ctx.objs, target_thread, sig)
151 }
152153/// Send a signal to `target_thread` from the thread and process in `objs`. A signal of 0 will
154 /// be ignored.
155fn signal_thread(
156 objs: &ThreadContext,
157 target_thread: &Thread,
158 signal: std::ffi::c_int,
159 ) -> Result<(), Errno> {
160if signal == 0 {
161return Ok(());
162 }
163164let Ok(signal) = Signal::try_from(signal) else {
165return Err(Errno::EINVAL);
166 };
167168if signal.is_realtime() {
169log::warn!("Unimplemented signal {signal:?}");
170return Err(Errno::ENOTSUP);
171 }
172173// need to scope the shmem lock since `wakeup_for_signal` below takes its own shmem lock
174let mut cond = {
175let shmem_lock = &*objs.host.shim_shmem_lock_borrow().unwrap();
176177let target_process = objs
178 .host
179 .process_borrow(target_thread.process_id())
180 .unwrap();
181let target_process = &*target_process.borrow(objs.host.root());
182183let process_shmem = target_process.borrow_as_runnable().unwrap();
184let process_shmem = &*process_shmem.shmem();
185let process_protected = process_shmem.protected.borrow(&shmem_lock.root);
186187let thread_shmem = target_thread.shmem();
188let mut thread_protected = thread_shmem.protected.borrow_mut(&shmem_lock.root);
189190let action = unsafe { process_protected.signal_action(signal) };
191let action_handler = unsafe { action.handler() };
192193let signal_is_ignored = match action_handler {
194 SignalHandler::SigIgn => true,
195 SignalHandler::SigDfl => defaultaction(signal) == LinuxDefaultAction::IGN,
196_ => false,
197 };
198199if signal_is_ignored {
200// don't deliver an ignored signal
201return Ok(());
202 }
203204if thread_protected.pending_signals.has(signal) {
205// Signal is already pending. From signal(7): In the case where a standard signal is
206 // already pending, the siginfo_t structure (see sigaction(2)) associated with that
207 // signal is not overwritten on arrival of subsequent instances of the same signal.
208return Ok(());
209 }
210211 thread_protected.pending_signals.add(signal);
212213let sender_pid = objs.process.id();
214let sender_tid = objs.thread.id();
215216let siginfo = siginfo_t::new_for_tkill(signal, sender_pid.into(), 0);
217218 thread_protected.set_pending_standard_siginfo(signal, &siginfo);
219220if sender_tid == target_thread.id() {
221// Target is the current thread. It'll be handled synchronously when the current
222 // syscall returns (if it's unblocked).
223return Ok(());
224 }
225226if thread_protected.blocked_signals.has(signal) {
227// Target thread has the signal blocked. We'll leave it pending, but no need to
228 // schedule an event to process the signal. It'll get processed synchronously when
229 // the thread executes a syscall that would unblock the signal.
230return Ok(());
231 }
232233let Some(cond) = target_thread.syscall_condition_mut() else {
234// We may be able to get here if a thread is signalled before it runs for the first
235 // time. Just return; the signal will be delivered when the thread runs.
236return Ok(());
237 };
238239 cond
240 };
241242let was_scheduled = cond.wakeup_for_signal(objs.host, signal);
243244// it won't be scheduled if the signal is blocked, but we previously checked if the signal
245 // was blocked above
246assert!(was_scheduled);
247248Ok(())
249 }
250251log_syscall!(
252 rt_sigaction,
253/* rv */ std::ffi::c_int,
254/* sig */ std::ffi::c_int,
255/* act */ *const std::ffi::c_void,
256/* oact */ *const std::ffi::c_void,
257/* sigsetsize */ libc::size_t,
258 );
259pub fn rt_sigaction(
260 ctx: &mut SyscallContext,
261 sig: std::ffi::c_int,
262 act: ForeignPtr<linux_api::signal::sigaction>,
263 oact: ForeignPtr<linux_api::signal::sigaction>,
264 sigsetsize: libc::size_t,
265 ) -> Result<(), Errno> {
266// rt_sigaction(2):
267 // > Consequently, a new system call, rt_sigaction(), was added to support an enlarged
268 // > sigset_t type. The new system call takes a fourth argument, size_t sigsetsize, which
269 // > specifies the size in bytes of the signal sets in act.sa_mask and oldact.sa_mask. This
270 // > argument is currently required to have the value sizeof(sigset_t) (or the error EINVAL
271 // > results)
272 // Assuming by "sizeof(sigset_t)" it means the kernel's `linux_sigset_t` and not glibc's
273 // `sigset_t`...
274if sigsetsize != size_of::<linux_api::signal::sigset_t>() {
275return Err(Errno::EINVAL);
276 }
277278let Ok(sig) = Signal::try_from(sig) else {
279return Err(Errno::EINVAL);
280 };
281282let shmem_lock = ctx.objs.host.shim_shmem_lock_borrow().unwrap();
283let process_shmem = ctx.objs.process.shmem();
284let mut process_protected = process_shmem.protected.borrow_mut(&shmem_lock.root);
285286if !oact.is_null() {
287let old_action = unsafe { process_protected.signal_action(sig) };
288 ctx.objs
289 .process
290 .memory_borrow_mut()
291 .write(oact, old_action)?;
292 }
293294if act.is_null() {
295// nothing left to do
296return Ok(());
297 }
298299if sig == Signal::SIGKILL || sig == Signal::SIGSTOP {
300return Err(Errno::EINVAL);
301 }
302303let new_action = ctx.objs.process.memory_borrow().read(act)?;
304unsafe { *process_protected.signal_action_mut(sig) = new_action };
305306Ok(())
307 }
308309log_syscall!(
310 rt_sigprocmask,
311/* rv */ std::ffi::c_int,
312/* how */ std::ffi::c_int,
313/* nset */ *const std::ffi::c_void,
314/* oset */ *const std::ffi::c_void,
315/* sigsetsize */ libc::size_t,
316 );
317pub fn rt_sigprocmask(
318 ctx: &mut SyscallContext,
319 how: std::ffi::c_int,
320 nset: ForeignPtr<linux_api::signal::sigset_t>,
321 oset: ForeignPtr<linux_api::signal::sigset_t>,
322 sigsetsize: libc::size_t,
323 ) -> Result<(), Errno> {
324// From sigprocmask(2): This argument is currently required to have a fixed architecture
325 // specific value (equal to sizeof(kernel_sigset_t)).
326 // We use `sigset_t` from `linux_api`, which is a wrapper around `linux_sigset_t` from
327 // the kernel and should be equivalent to `kernel_sigset_t`.
328if sigsetsize != size_of::<linux_api::signal::sigset_t>() {
329warn_once_then_debug!("Bad sigsetsize {sigsetsize}");
330return Err(Errno::EINVAL);
331 }
332333let shmem_lock = ctx.objs.host.shim_shmem_lock_borrow().unwrap();
334let thread_shmem = ctx.objs.thread.shmem();
335let mut thread_protected = thread_shmem.protected.borrow_mut(&shmem_lock.root);
336337let current_set = thread_protected.blocked_signals;
338339if !oset.is_null() {
340 ctx.objs
341 .process
342 .memory_borrow_mut()
343 .write(oset, ¤t_set)?;
344 }
345346if nset.is_null() {
347// nothing left to do
348return Ok(());
349 }
350351let set = ctx.objs.process.memory_borrow().read(nset)?;
352353let set = match SigProcMaskAction::try_from(how) {
354Ok(SigProcMaskAction::SIG_BLOCK) => set | current_set,
355Ok(SigProcMaskAction::SIG_UNBLOCK) => !set & current_set,
356Ok(SigProcMaskAction::SIG_SETMASK) => set,
357Err(_) => return Err(Errno::EINVAL),
358 };
359360 thread_protected.blocked_signals = set;
361362Ok(())
363 }
364365log_syscall!(
366 sigaltstack,
367/* rv */ std::ffi::c_int,
368/* uss */ *const std::ffi::c_void,
369/* uoss */ *const std::ffi::c_void,
370 );
371pub fn sigaltstack(
372 ctx: &mut SyscallContext,
373 uss: ForeignPtr<linux_api::signal::stack_t>,
374 uoss: ForeignPtr<linux_api::signal::stack_t>,
375 ) -> Result<(), Errno> {
376let shmem_lock = ctx.objs.host.shim_shmem_lock_borrow().unwrap();
377let thread_shmem = ctx.objs.thread.shmem();
378let mut thread_protected = thread_shmem.protected.borrow_mut(&shmem_lock.root);
379380let old_ss = unsafe { *thread_protected.sigaltstack() };
381382if !uss.is_null() {
383if old_ss.flags_retain().contains(SigAltStackFlags::SS_ONSTACK) {
384// sigaltstack(2): EPERM An attempt was made to change the
385 // alternate signal stack while it was active.
386return Err(Errno::EPERM);
387 }
388389let mut new_ss = ctx.objs.process.memory_borrow().read(uss)?;
390if new_ss.flags_retain().contains(SigAltStackFlags::SS_DISABLE) {
391// sigaltstack(2): To disable an existing stack, specify ss.ss_flags
392 // as SS_DISABLE. In this case, the kernel ignores any other flags
393 // in ss.ss_flags and the remaining fields in ss.
394new_ss = shadow_pod::zeroed();
395 new_ss.ss_flags = SigAltStackFlags::SS_DISABLE.bits();
396 }
397398let unrecognized_flags = new_ss
399 .flags_retain()
400 .difference(SigAltStackFlags::SS_DISABLE | SigAltStackFlags::SS_AUTODISARM);
401402if !unrecognized_flags.is_empty() {
403warn_once_then_debug!(
404"Unrecognized signal stack flags {unrecognized_flags:?} in {:?}",
405 new_ss.flags_retain(),
406 );
407// Unrecognized flag.
408return Err(Errno::EINVAL);
409 }
410411*unsafe { thread_protected.sigaltstack_mut() } = new_ss;
412 }
413414// It may look wrong that we can return EFAULT here even after we updated the alt stack
415 // above, but this is how Linux behaves.
416if !uoss.is_null() {
417 ctx.objs.process.memory_borrow_mut().write(uoss, &old_ss)?;
418 }
419420Ok(())
421 }
422}
423424#[cfg(test)]
425mod test {
426use super::*;
427428/// The bitflags crate does some weird things with unrecognized flags, so this test ensures that
429 /// they work as expected for how we use them above.
430#[test]
431fn unrecognized_flags_difference() {
432let foo = 1 << 28;
433// ensure this flag is unused
434assert_eq!(SigAltStackFlags::from_bits(foo), None);
435let foo = SigAltStackFlags::from_bits_retain(foo);
436assert_eq!(
437 foo.difference(SigAltStackFlags::SS_DISABLE).bits(),
438 (1 << 28) & !SigAltStackFlags::SS_DISABLE.bits(),
439 );
440assert_eq!(
441 (foo - SigAltStackFlags::SS_DISABLE).bits(),
442 (1 << 28) & !SigAltStackFlags::SS_DISABLE.bits(),
443 );
444 }
445446#[test]
447fn unrecognized_flags_empty() {
448let foo = 1 << 28;
449assert_ne!(foo, 0);
450// ensure this flag is unused
451assert_eq!(SigAltStackFlags::from_bits(foo), None);
452let foo = SigAltStackFlags::from_bits_retain(foo);
453assert_ne!(foo.bits(), 0);
454assert!(!foo.is_empty());
455 }
456}