1use linux_api::errno::Errno;
2use linux_api::signal::{LinuxDefaultAction, Signal, SignalHandler, defaultaction, siginfo_t};
3use shadow_shim_helper_rs::explicit_drop::{ExplicitDrop, ExplicitDropper};
4use shadow_shim_helper_rs::syscall_types::ForeignPtr;
56use crate::cshadow as c;
7use crate::host::process::Process;
8use crate::host::syscall::handler::{SyscallContext, SyscallHandler, ThreadContext};
9use crate::host::syscall::types::SyscallError;
10use crate::host::thread::Thread;
1112impl SyscallHandler {
13log_syscall!(
14 kill,
15/* rv */ std::ffi::c_int,
16/* pid */ linux_api::posix_types::kernel_pid_t,
17/* sig */ std::ffi::c_int,
18 );
19pub fn kill(
20 ctx: &mut SyscallContext,
21 pid: linux_api::posix_types::kernel_pid_t,
22 sig: std::ffi::c_int,
23 ) -> Result<(), Errno> {
24log::trace!("kill called on pid {pid} with signal {sig}");
2526let pid = if pid == -1 {
27// kill(2): If pid equals -1, then sig is sent to every process for which the calling
28 // process has permission to send signals, except for process 1.
29 //
30 // Currently unimplemented, and unlikely to be needed in the context of a shadow
31 // simulation.
32log::warn!("kill with pid=-1 unimplemented");
33return Err(Errno::ENOTSUP);
34 } else if pid == 0 {
35// kill(2): If pid equals 0, then sig is sent to every process in the process group of
36 // the calling process.
37 //
38 // Currently every emulated process is in its own process group.
39 //
40 // FIXME: The above comment is no longer true since implementing fork(). See
41 // https://github.com/shadow/shadow/issues/3315
42ctx.objs.process.id()
43 } else if pid < -1 {
44// kill(2): If pid is less than -1, then sig is sent to every process in the process
45 // group whose ID is -pid.
46 //
47 // Currently every emulated process is in its own process group, where pgid=pid.
48 //
49 // FIXME: The above comment is no longer true since implementing fork(). See
50 // https://github.com/shadow/shadow/issues/3315
51(-pid).try_into().or(Err(Errno::ESRCH))?
52} else {
53 pid.try_into().or(Err(Errno::ESRCH))?
54};
5556let Some(target_process) = ctx.objs.host.process_borrow(pid) else {
57log::debug!("Process {pid} not found");
58return Err(Errno::ESRCH);
59 };
60let target_process = &*target_process.borrow(ctx.objs.host.root());
6162Self::signal_process(ctx.objs, target_process, sig)
63 }
6465/// Send a signal to `target_process` from the thread and process in `objs`. A signal of 0 will
66 /// be ignored.
67fn signal_process(
68 objs: &ThreadContext,
69 target_process: &Process,
70 signal: std::ffi::c_int,
71 ) -> Result<(), Errno> {
72if signal == 0 {
73return Ok(());
74 }
7576let Ok(signal) = Signal::try_from(signal) else {
77return Err(Errno::EINVAL);
78 };
7980if signal.is_realtime() {
81log::warn!("Unimplemented signal {signal:?}");
82return Err(Errno::ENOTSUP);
83 }
8485let sender_pid = objs.process.id().into();
86let siginfo = siginfo_t::new_for_kill(signal, sender_pid, 0);
8788 target_process.signal(objs.host, Some(objs.thread), &siginfo);
8990Ok(())
91 }
9293log_syscall!(
94 tkill,
95/* rv */ std::ffi::c_int,
96/* pid */ linux_api::posix_types::kernel_pid_t,
97/* sig */ std::ffi::c_int,
98 );
99pub fn tkill(
100 ctx: &mut SyscallContext,
101 tid: linux_api::posix_types::kernel_pid_t,
102 sig: std::ffi::c_int,
103 ) -> Result<(), Errno> {
104log::trace!("tkill called on tid {tid} with signal {sig}");
105106let tid = tid.try_into().or(Err(Errno::ESRCH))?;
107108let Some(target_thread) = ctx.objs.host.thread_cloned_rc(tid) else {
109return Err(Errno::ESRCH);
110 };
111let target_thread = ExplicitDropper::new(target_thread, |value| {
112 value.explicit_drop(ctx.objs.host.root())
113 });
114let target_thread = &*target_thread.borrow(ctx.objs.host.root());
115116Self::signal_thread(ctx.objs, target_thread, sig)
117 }
118119log_syscall!(
120 tgkill,
121/* rv */ std::ffi::c_int,
122/* tgid */ linux_api::posix_types::kernel_pid_t,
123/* pid */ linux_api::posix_types::kernel_pid_t,
124/* sig */ std::ffi::c_int,
125 );
126pub fn tgkill(
127 ctx: &mut SyscallContext,
128 tgid: linux_api::posix_types::kernel_pid_t,
129 tid: linux_api::posix_types::kernel_pid_t,
130 sig: std::ffi::c_int,
131 ) -> Result<(), Errno> {
132log::trace!("tgkill called on tgid {tgid} and tid {tid} with signal {sig}");
133134let tgid = tgid.try_into().or(Err(Errno::ESRCH))?;
135let tid = tid.try_into().or(Err(Errno::ESRCH))?;
136137let Some(target_thread) = ctx.objs.host.thread_cloned_rc(tid) else {
138return Err(Errno::ESRCH);
139 };
140let target_thread = ExplicitDropper::new(target_thread, |value| {
141 value.explicit_drop(ctx.objs.host.root())
142 });
143let target_thread = &*target_thread.borrow(ctx.objs.host.root());
144145if target_thread.process_id() != tgid {
146return Err(Errno::ESRCH);
147 }
148149Self::signal_thread(ctx.objs, target_thread, sig)
150 }
151152/// Send a signal to `target_thread` from the thread and process in `objs`. A signal of 0 will
153 /// be ignored.
154fn signal_thread(
155 objs: &ThreadContext,
156 target_thread: &Thread,
157 signal: std::ffi::c_int,
158 ) -> Result<(), Errno> {
159if signal == 0 {
160return Ok(());
161 }
162163let Ok(signal) = Signal::try_from(signal) else {
164return Err(Errno::EINVAL);
165 };
166167if signal.is_realtime() {
168log::warn!("Unimplemented signal {signal:?}");
169return Err(Errno::ENOTSUP);
170 }
171172// need to scope the shmem lock since `wakeup_for_signal` below takes its own shmem lock
173let mut cond = {
174let shmem_lock = &*objs.host.shim_shmem_lock_borrow().unwrap();
175176let target_process = objs
177 .host
178 .process_borrow(target_thread.process_id())
179 .unwrap();
180let target_process = &*target_process.borrow(objs.host.root());
181182let process_shmem = target_process.borrow_as_runnable().unwrap();
183let process_shmem = &*process_shmem.shmem();
184let process_protected = process_shmem.protected.borrow(&shmem_lock.root);
185186let thread_shmem = target_thread.shmem();
187let mut thread_protected = thread_shmem.protected.borrow_mut(&shmem_lock.root);
188189let action = unsafe { process_protected.signal_action(signal) };
190let action_handler = unsafe { action.handler() };
191192let signal_is_ignored = match action_handler {
193 SignalHandler::SigIgn => true,
194 SignalHandler::SigDfl => defaultaction(signal) == LinuxDefaultAction::IGN,
195_ => false,
196 };
197198if signal_is_ignored {
199// don't deliver an ignored signal
200return Ok(());
201 }
202203if thread_protected.pending_signals.has(signal) {
204// Signal is already pending. From signal(7): In the case where a standard signal is
205 // already pending, the siginfo_t structure (see sigaction(2)) associated with that
206 // signal is not overwritten on arrival of subsequent instances of the same signal.
207return Ok(());
208 }
209210 thread_protected.pending_signals.add(signal);
211212let sender_pid = objs.process.id();
213let sender_tid = objs.thread.id();
214215let siginfo = siginfo_t::new_for_tkill(signal, sender_pid.into(), 0);
216217 thread_protected.set_pending_standard_siginfo(signal, &siginfo);
218219if sender_tid == target_thread.id() {
220// Target is the current thread. It'll be handled synchronously when the current
221 // syscall returns (if it's unblocked).
222return Ok(());
223 }
224225if thread_protected.blocked_signals.has(signal) {
226// Target thread has the signal blocked. We'll leave it pending, but no need to
227 // schedule an event to process the signal. It'll get processed synchronously when
228 // the thread executes a syscall that would unblock the signal.
229return Ok(());
230 }
231232let Some(cond) = target_thread.syscall_condition_mut() else {
233// We may be able to get here if a thread is signalled before it runs for the first
234 // time. Just return; the signal will be delivered when the thread runs.
235return Ok(());
236 };
237238 cond
239 };
240241let was_scheduled = cond.wakeup_for_signal(objs.host, signal);
242243// it won't be scheduled if the signal is blocked, but we previously checked if the signal
244 // was blocked above
245assert!(was_scheduled);
246247Ok(())
248 }
249250log_syscall!(
251 rt_sigaction,
252/* rv */ std::ffi::c_int,
253/* sig */ std::ffi::c_int,
254/* act */ *const std::ffi::c_void,
255/* oact */ *const std::ffi::c_void,
256/* sigsetsize */ libc::size_t,
257 );
258pub fn rt_sigaction(
259 ctx: &mut SyscallContext,
260 _sig: std::ffi::c_int,
261 _act: ForeignPtr<linux_api::signal::sigaction>,
262 _oact: ForeignPtr<linux_api::signal::sigaction>,
263 _sigsetsize: libc::size_t,
264 ) -> Result<(), SyscallError> {
265let rv: i32 = Self::legacy_syscall(c::syscallhandler_rt_sigaction, ctx)?;
266assert_eq!(rv, 0);
267Ok(())
268 }
269270log_syscall!(
271 rt_sigprocmask,
272/* rv */ std::ffi::c_int,
273/* how */ std::ffi::c_int,
274/* nset */ *const std::ffi::c_void,
275/* oset */ *const std::ffi::c_void,
276/* sigsetsize */ libc::size_t,
277 );
278pub fn rt_sigprocmask(
279 ctx: &mut SyscallContext,
280 _how: std::ffi::c_int,
281 _nset: ForeignPtr<linux_api::signal::sigset_t>,
282 _oset: ForeignPtr<linux_api::signal::sigset_t>,
283 _sigsetsize: libc::size_t,
284 ) -> Result<(), SyscallError> {
285let rv: i32 = Self::legacy_syscall(c::syscallhandler_rt_sigprocmask, ctx)?;
286assert_eq!(rv, 0);
287Ok(())
288 }
289290log_syscall!(
291 sigaltstack,
292/* rv */ std::ffi::c_int,
293/* uss */ *const std::ffi::c_void,
294/* uoss */ *const std::ffi::c_void,
295 );
296pub fn sigaltstack(
297 ctx: &mut SyscallContext,
298 _uss: ForeignPtr<linux_api::signal::stack_t>,
299 _uoss: ForeignPtr<linux_api::signal::stack_t>,
300 ) -> Result<(), SyscallError> {
301let rv: i32 = Self::legacy_syscall(c::syscallhandler_sigaltstack, ctx)?;
302assert_eq!(rv, 0);
303Ok(())
304 }
305}