shadow_rs/host/syscall/handler/
signal.rs

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;
5
6use 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;
11
12impl SyscallHandler {
13    log_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    );
19    pub fn kill(
20        ctx: &mut SyscallContext,
21        pid: linux_api::posix_types::kernel_pid_t,
22        sig: std::ffi::c_int,
23    ) -> Result<(), Errno> {
24        log::trace!("kill called on pid {pid} with signal {sig}");
25
26        let 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.
32            log::warn!("kill with pid=-1 unimplemented");
33            return 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
42            ctx.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        };
55
56        let Some(target_process) = ctx.objs.host.process_borrow(pid) else {
57            log::debug!("Process {pid} not found");
58            return Err(Errno::ESRCH);
59        };
60        let target_process = &*target_process.borrow(ctx.objs.host.root());
61
62        Self::signal_process(ctx.objs, target_process, sig)
63    }
64
65    /// Send a signal to `target_process` from the thread and process in `objs`. A signal of 0 will
66    /// be ignored.
67    fn signal_process(
68        objs: &ThreadContext,
69        target_process: &Process,
70        signal: std::ffi::c_int,
71    ) -> Result<(), Errno> {
72        if signal == 0 {
73            return Ok(());
74        }
75
76        let Ok(signal) = Signal::try_from(signal) else {
77            return Err(Errno::EINVAL);
78        };
79
80        if signal.is_realtime() {
81            log::warn!("Unimplemented signal {signal:?}");
82            return Err(Errno::ENOTSUP);
83        }
84
85        let sender_pid = objs.process.id().into();
86        let siginfo = siginfo_t::new_for_kill(signal, sender_pid, 0);
87
88        target_process.signal(objs.host, Some(objs.thread), &siginfo);
89
90        Ok(())
91    }
92
93    log_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    );
99    pub fn tkill(
100        ctx: &mut SyscallContext,
101        tid: linux_api::posix_types::kernel_pid_t,
102        sig: std::ffi::c_int,
103    ) -> Result<(), Errno> {
104        log::trace!("tkill called on tid {tid} with signal {sig}");
105
106        let tid = tid.try_into().or(Err(Errno::ESRCH))?;
107
108        let Some(target_thread) = ctx.objs.host.thread_cloned_rc(tid) else {
109            return Err(Errno::ESRCH);
110        };
111        let target_thread = ExplicitDropper::new(target_thread, |value| {
112            value.explicit_drop(ctx.objs.host.root())
113        });
114        let target_thread = &*target_thread.borrow(ctx.objs.host.root());
115
116        Self::signal_thread(ctx.objs, target_thread, sig)
117    }
118
119    log_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    );
126    pub 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> {
132        log::trace!("tgkill called on tgid {tgid} and tid {tid} with signal {sig}");
133
134        let tgid = tgid.try_into().or(Err(Errno::ESRCH))?;
135        let tid = tid.try_into().or(Err(Errno::ESRCH))?;
136
137        let Some(target_thread) = ctx.objs.host.thread_cloned_rc(tid) else {
138            return Err(Errno::ESRCH);
139        };
140        let target_thread = ExplicitDropper::new(target_thread, |value| {
141            value.explicit_drop(ctx.objs.host.root())
142        });
143        let target_thread = &*target_thread.borrow(ctx.objs.host.root());
144
145        if target_thread.process_id() != tgid {
146            return Err(Errno::ESRCH);
147        }
148
149        Self::signal_thread(ctx.objs, target_thread, sig)
150    }
151
152    /// Send a signal to `target_thread` from the thread and process in `objs`. A signal of 0 will
153    /// be ignored.
154    fn signal_thread(
155        objs: &ThreadContext,
156        target_thread: &Thread,
157        signal: std::ffi::c_int,
158    ) -> Result<(), Errno> {
159        if signal == 0 {
160            return Ok(());
161        }
162
163        let Ok(signal) = Signal::try_from(signal) else {
164            return Err(Errno::EINVAL);
165        };
166
167        if signal.is_realtime() {
168            log::warn!("Unimplemented signal {signal:?}");
169            return Err(Errno::ENOTSUP);
170        }
171
172        // need to scope the shmem lock since `wakeup_for_signal` below takes its own shmem lock
173        let mut cond = {
174            let shmem_lock = &*objs.host.shim_shmem_lock_borrow().unwrap();
175
176            let target_process = objs
177                .host
178                .process_borrow(target_thread.process_id())
179                .unwrap();
180            let target_process = &*target_process.borrow(objs.host.root());
181
182            let process_shmem = target_process.borrow_as_runnable().unwrap();
183            let process_shmem = &*process_shmem.shmem();
184            let process_protected = process_shmem.protected.borrow(&shmem_lock.root);
185
186            let thread_shmem = target_thread.shmem();
187            let mut thread_protected = thread_shmem.protected.borrow_mut(&shmem_lock.root);
188
189            let action = unsafe { process_protected.signal_action(signal) };
190            let action_handler = unsafe { action.handler() };
191
192            let signal_is_ignored = match action_handler {
193                SignalHandler::SigIgn => true,
194                SignalHandler::SigDfl => defaultaction(signal) == LinuxDefaultAction::IGN,
195                _ => false,
196            };
197
198            if signal_is_ignored {
199                // don't deliver an ignored signal
200                return Ok(());
201            }
202
203            if 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.
207                return Ok(());
208            }
209
210            thread_protected.pending_signals.add(signal);
211
212            let sender_pid = objs.process.id();
213            let sender_tid = objs.thread.id();
214
215            let siginfo = siginfo_t::new_for_tkill(signal, sender_pid.into(), 0);
216
217            thread_protected.set_pending_standard_siginfo(signal, &siginfo);
218
219            if 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).
222                return Ok(());
223            }
224
225            if 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.
229                return Ok(());
230            }
231
232            let 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.
235                return Ok(());
236            };
237
238            cond
239        };
240
241        let was_scheduled = cond.wakeup_for_signal(objs.host, signal);
242
243        // it won't be scheduled if the signal is blocked, but we previously checked if the signal
244        // was blocked above
245        assert!(was_scheduled);
246
247        Ok(())
248    }
249
250    log_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    );
258    pub 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> {
265        let rv: i32 = Self::legacy_syscall(c::syscallhandler_rt_sigaction, ctx)?;
266        assert_eq!(rv, 0);
267        Ok(())
268    }
269
270    log_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    );
278    pub 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> {
285        let rv: i32 = Self::legacy_syscall(c::syscallhandler_rt_sigprocmask, ctx)?;
286        assert_eq!(rv, 0);
287        Ok(())
288    }
289
290    log_syscall!(
291        sigaltstack,
292        /* rv */ std::ffi::c_int,
293        /* uss */ *const std::ffi::c_void,
294        /* uoss */ *const std::ffi::c_void,
295    );
296    pub 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> {
301        let rv: i32 = Self::legacy_syscall(c::syscallhandler_sigaltstack, ctx)?;
302        assert_eq!(rv, 0);
303        Ok(())
304    }
305}