shadow_shim/
signals.rs

1use core::cell::Cell;
2
3use linux_api::signal::{
4    SigActionFlags, SigAltStackFlags, Signal, SignalHandler, defaultaction, sigaction, siginfo_t,
5    sigset_t, stack_t,
6};
7use linux_api::ucontext::ucontext;
8use log::{trace, warn};
9use shadow_shim_helper_rs::shim_shmem;
10
11use crate::tls::ShimTlsVar;
12use crate::{ExecutionContext, global_host_shmem, tls_process_shmem, tls_thread_shmem};
13
14/// Information passed through to the SIGUSR1 signal handler. Contains the info
15/// needed to call a managed code signal handler.
16struct Sigusr1Info {
17    native_sigaltstack: Option<stack_t>,
18    siginfo: siginfo_t,
19    action: sigaction,
20    // May be NULL, in the case that we didn't get here in the context of an
21    // earlier signal handler (e.g. seccomp).
22
23    // We don't copy by value in case additional fields are added to the stuct
24    // definition, and because we currently accept a libc::ucontext_t in our C
25    // API, which *does* have extra fields at the end.
26    ctx: *mut ucontext,
27}
28static SIGUSR1_SIGINFO: ShimTlsVar<Cell<Option<Sigusr1Info>>> =
29    ShimTlsVar::new(&crate::SHIM_TLS, || Cell::new(None));
30
31extern "C" fn handle_sigusr1(_signo: i32, _info: *mut siginfo_t, ctx: *mut core::ffi::c_void) {
32    debug_assert_eq!(
33        ExecutionContext::current(),
34        ExecutionContext::Shadow,
35        "Native sigusr1 unexpectedly raised from non-shadow code"
36    );
37
38    let mut info = SIGUSR1_SIGINFO.get().take().unwrap();
39    let signo = info.siginfo.signal().unwrap().as_i32();
40    // SAFETY: Should have been initialized correctly in `process_signals`.
41    let handler = unsafe { info.action.handler() };
42
43    if let Some(stack) = &info.native_sigaltstack {
44        // We temporarily switched the sigaltstack so that this handler would
45        // run on the specified stack. Now switch back to the native sigaltstack (i.e.
46        // the one Shadow originally configured for *it's* signal handling).
47        unsafe { linux_api::signal::sigaltstack(Some(stack), None) }.unwrap();
48    }
49
50    // SAFETY: Not particularly. We're calling a handler provided by managed code, which
51    // we don't attempt to analyze or sandbox. A "well behaved" handler should be safe to
52    // call here, but it could do anything including things that are unsound in Rust.
53    match handler {
54        linux_api::signal::SignalHandler::Handler(handler_fn) => {
55            let _prev = ExecutionContext::Application.enter();
56            unsafe { handler_fn(signo) }
57        }
58        linux_api::signal::SignalHandler::Action(action_fn) => unsafe {
59            // If there's an "earlier" context, we use it. This might be important e.g.
60            // when handling a signal like SIGSEGV, where the handler might actually
61            // inspect individual register values.
62            //
63            // Otherwise, use the the context that the kernel gave us for *this* signal
64            // handler.  The register values won't make much sense to the handler, but
65            // it should WAI with functionality like `swapcontext`, which might be done
66            // in an implementation of user-space threads.
67            let ctx: *mut ucontext = if info.ctx.is_null() {
68                log::warn!("Passing a synthetic context to managed code signal handler");
69                ctx.cast()
70            } else {
71                info.ctx
72            };
73            {
74                let _prev = ExecutionContext::Application.enter();
75                action_fn(signo, &mut info.siginfo, ctx.cast::<core::ffi::c_void>())
76            }
77        },
78        linux_api::signal::SignalHandler::SigIgn | linux_api::signal::SignalHandler::SigDfl => {
79            panic!("No handler")
80        }
81    }
82}
83
84fn die_with_fatal_signal(sig: Signal) -> ! {
85    assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
86    if sig == Signal::SIGKILL {
87        // No need to restore default action, and trying to do so would fail.
88    } else {
89        let action = sigaction::new_with_default_restorer(
90            SignalHandler::SigDfl,
91            SigActionFlags::empty(),
92            sigset_t::EMPTY,
93        );
94        unsafe { linux_api::signal::rt_sigaction(sig, &action, None) }.unwrap();
95    }
96    let pid = rustix::process::getpid();
97    rustix::process::kill_process(pid, rustix::process::Signal::from_raw(sig.into()).unwrap())
98        .unwrap();
99    unreachable!()
100}
101
102/// Handle pending unblocked signals, and return whether *all* corresponding
103/// signal actions had the SA_RESTART flag set.
104///
105/// # Safety
106///
107/// Configured handlers for all pending unblocked signals must be safe to call. (Which
108/// we basically can't ensure).
109pub unsafe fn process_signals(mut ucontext: Option<&mut ucontext>) -> bool {
110    debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
111
112    let mut host = crate::global_host_shmem::get();
113    let mut host_lock = host.protected().lock();
114
115    let mut restartable = true;
116
117    loop {
118        let Some((sig, siginfo)) = tls_process_shmem::with(|process| {
119            tls_thread_shmem::with(|thread| {
120                shim_shmem::take_pending_unblocked_signal(&host_lock, process, thread)
121            })
122        }) else {
123            break;
124        };
125
126        let action = tls_process_shmem::with(|process| unsafe {
127            *process.protected.borrow(&host_lock.root).signal_action(sig)
128        });
129
130        if matches!(unsafe { action.handler() }, SignalHandler::SigIgn) {
131            continue;
132        }
133
134        if matches!(unsafe { action.handler() }, SignalHandler::SigDfl) {
135            match defaultaction(sig) {
136                linux_api::signal::LinuxDefaultAction::IGN => continue,
137                linux_api::signal::LinuxDefaultAction::CORE
138                | linux_api::signal::LinuxDefaultAction::TERM => {
139                    drop(host_lock);
140                    die_with_fatal_signal(sig);
141                }
142                linux_api::signal::LinuxDefaultAction::STOP => unimplemented!(),
143                linux_api::signal::LinuxDefaultAction::CONT => unimplemented!(),
144            }
145        }
146
147        trace!("Handling emulated signal {sig:?}");
148
149        let (sigaltstack_orig_emu, mask_orig_emu): (stack_t, sigset_t) =
150            tls_thread_shmem::with(|thread| {
151                let t = thread.protected.borrow(&host_lock.root);
152                // SAFETY: Pointers in the sigaltstack are valid in the managed process.
153                let stack = unsafe { t.sigaltstack() };
154                (*stack, t.blocked_signals)
155            });
156
157        let mask_emu_during_handler = {
158            let mut m = action.mask() | mask_orig_emu;
159            if !action.flags_retain().contains(SigActionFlags::SA_NODEFER) {
160                m.add(sig)
161            }
162            m
163        };
164        tls_thread_shmem::with(|thread| {
165            thread.protected.borrow_mut(&host_lock.root).blocked_signals = mask_emu_during_handler
166        });
167
168        if action.flags_retain().contains(SigActionFlags::SA_RESETHAND) {
169            tls_process_shmem::with(|process| {
170                // SAFETY: The handler (`SigDfl`) is sound.
171                unsafe {
172                    *process
173                        .protected
174                        .borrow_mut(&host_lock.root)
175                        .signal_action_mut(sig) = sigaction::new_with_default_restorer(
176                        SignalHandler::SigDfl,
177                        SigActionFlags::empty(),
178                        sigset_t::EMPTY,
179                    )
180                };
181            });
182        }
183
184        if !action.flags_retain().contains(SigActionFlags::SA_RESTART) {
185            restartable = false;
186        }
187
188        let sigaltstack_orig_native = if action.flags_retain().contains(SigActionFlags::SA_ONSTACK)
189            && !sigaltstack_orig_emu
190                .flags_retain()
191                .contains(SigAltStackFlags::SS_DISABLE)
192        {
193            // Call the handler on the configured stack.
194
195            if sigaltstack_orig_emu
196                .flags_retain()
197                .contains(SigAltStackFlags::SS_ONSTACK)
198            {
199                // The specified stack is already in use.
200                //
201                // This *could* be ok, e.g. if the stack is in use by the
202                // current thread, and never unwound back to the earlier use;
203                // e.g. if the handler exits the process. golang appears to do
204                // this in its default SIGTERM handling. (See
205                // https://github.com/shadow/shadow/issues/3395).
206                //
207                // In other cases things could go horribly, but it'd be a bug in
208                // the managed process rather than in shadow itself.
209                log::debug!(
210                    "Signal handler configured to switch to a stack that's already in use. This could go badly."
211                )
212            }
213
214            // Update the signal-stack configuration while the handler is being run.
215            let sigaltstack_emu_during_handler = if sigaltstack_orig_emu
216                .flags_retain()
217                .contains(SigAltStackFlags::SS_AUTODISARM)
218            {
219                stack_t::new(core::ptr::null_mut(), SigAltStackFlags::SS_DISABLE, 0)
220            } else {
221                stack_t::new(
222                    sigaltstack_orig_emu.sp(),
223                    sigaltstack_orig_emu.flags_retain() | SigAltStackFlags::SS_ONSTACK,
224                    sigaltstack_orig_emu.size(),
225                )
226            };
227            tls_thread_shmem::with(|thread| {
228                // SAFETY: stack pointer in the assigned stack (if any) is valid in
229                // the managed process.
230                unsafe {
231                    *thread
232                        .protected
233                        .borrow_mut(&host_lock.root)
234                        .sigaltstack_mut() = sigaltstack_emu_during_handler
235                };
236            });
237
238            let mut sigaltstack_orig_native =
239                stack_t::new(core::ptr::null_mut(), SigAltStackFlags::empty(), 0);
240            // Set the *native* sigaltstack to the *emulated* sigaltstack,
241            // letting the kernel do the stack switch for us.
242            unsafe {
243                linux_api::signal::sigaltstack(
244                    Some(&stack_t::new(
245                        sigaltstack_orig_emu.sp(),
246                        SigAltStackFlags::SS_AUTODISARM,
247                        sigaltstack_orig_emu.size(),
248                    )),
249                    Some(&mut sigaltstack_orig_native),
250                )
251            }
252            .unwrap();
253            Some(sigaltstack_orig_native)
254        } else {
255            None
256        };
257
258        // Package up what our native signal handler will need to invoke the
259        // managed code syscall handler for the emulated signal.
260        let prev = SIGUSR1_SIGINFO.get().replace(Some(Sigusr1Info {
261            native_sigaltstack: sigaltstack_orig_native,
262            siginfo,
263            action,
264            ctx: ucontext
265                .as_mut()
266                .map(|c| core::ptr::from_mut(*c))
267                .unwrap_or(core::ptr::null_mut()),
268        }));
269        assert!(prev.is_none());
270
271        // Drop locks and references, since the handler could do ~anything,
272        // including exit, recurse to here again, or `swapcontext` and never
273        // return.
274        drop(host_lock);
275        drop(host);
276
277        // We raise a signal natively to let the kernel create a ucontext for us
278        // and switch stacks. We invoke the managed code's signal handler from our
279        // signal handler.
280        //
281        // We could potentially skip this if the managed code signal handler isn't
282        // configured to switch stacks and either doesn't need a context or we already
283        // have one. But that'd mean another code path to maintain, and signal
284        // handling shouldn't be on the hot path of performance for most
285        // applications. (We could also consider implementing the stack switch
286        // and/or creation of a ucontext ourselves, but again that would be more
287        // complex code to maintain).
288
289        // We install the signal handler every time, so that we can decide
290        // whether to set `SA_ONSTACK` or not based on whether we actually need
291        // to switch stacks.
292        let flags = SigActionFlags::SA_SIGINFO
293            | SigActionFlags::SA_NODEFER
294            | SigActionFlags::SA_RESETHAND
295            | if sigaltstack_orig_native.is_some() {
296                SigActionFlags::SA_ONSTACK
297            } else {
298                SigActionFlags::empty()
299            };
300        // SAFETY: `handle_sigusr1` is sound, if the handler we're calling is.
301        unsafe {
302            linux_api::signal::rt_sigaction(
303                Signal::SIGUSR1,
304                &sigaction::new_with_default_restorer(
305                    SignalHandler::Action(handle_sigusr1),
306                    flags,
307                    sigset_t::EMPTY,
308                ),
309                None,
310            )
311        }
312        .unwrap();
313
314        let pid = rustix::process::getpid();
315        let tid = rustix::thread::gettid();
316        linux_api::signal::tgkill(pid.into(), tid.into(), Some(Signal::SIGUSR1)).unwrap();
317
318        // It's not unheard of for a signal handler to use setcontext to jump
319        // out of the signal handler to the point where the signal was raised
320        // instead of returning normally, e.g. for userspace scheduling.
321        // In that case we'll still be in `ExecutionContext::Application`, since
322        // the jump would have skipped over the execution context restorer's
323        // drop impl above. This is done in our `test_signals.rs` test.
324        //
325        // Force back to `ExecutionContext::Shadow`.
326        ExecutionContext::Shadow.enter_without_restorer();
327
328        // Reacquire locks and references.
329        host = crate::global_host_shmem::get();
330        host_lock = host.protected().lock();
331
332        // Restore mask and stack
333        tls_thread_shmem::with(|thread| {
334            let mut thread = thread.protected.borrow_mut(&host_lock.root);
335            thread.blocked_signals = mask_orig_emu;
336            // SAFETY: Pointers are valid in managed process.
337            unsafe { *thread.sigaltstack_mut() = sigaltstack_orig_emu };
338            if let Some(s) = sigaltstack_orig_native {
339                // SAFETY: We're restoring the previous, presumably valid, stack.
340                unsafe { linux_api::signal::sigaltstack(Some(&s), None) }.unwrap();
341            }
342        });
343    }
344    restartable
345}
346
347/// Handle a hardware error signal that was raised in `exe_ctx`.
348///
349/// # Safety
350///
351/// Configured handlers for all pending unblocked signals must be safe to call. (Which
352/// we basically can't ensure).
353unsafe fn handle_hardware_error_signal_inner(
354    exe_ctx: ExecutionContext,
355    signal: Signal,
356    info: &mut siginfo_t,
357    uctx: Option<&mut ucontext>,
358) {
359    if exe_ctx == ExecutionContext::Shadow {
360        // Error was raised from shim code.
361        die_with_fatal_signal(signal);
362    }
363
364    // Otherwise the error was raised from managed code, and could potentially
365    // be handled by a signal handler that it installed.
366
367    tls_thread_shmem::with(|thread| {
368        let host = global_host_shmem::get();
369        let host_lock = host.protected().lock();
370        let pending_signals = thread.protected.borrow(&host_lock.root).pending_signals;
371        if pending_signals.has(signal) {
372            warn!("Received signal {signal:?} when it was already pending");
373        } else {
374            let mut thread_protected = thread.protected.borrow_mut(&host_lock.root);
375            thread_protected.pending_signals |= signal.into();
376            thread_protected.set_pending_standard_siginfo(signal, info);
377        }
378    });
379
380    unsafe { process_signals(uctx) };
381}
382
383unsafe extern "C" fn handle_hardware_error_signal(
384    signo: i32,
385    info: *mut siginfo_t,
386    uctx: *mut core::ffi::c_void,
387) {
388    let prev_ctx = ExecutionContext::Shadow.enter();
389    let signal = Signal::try_from(signo).unwrap();
390    // SAFETY: The kernel should have given us a valid `siginfo_t` here.
391    let info = unsafe { info.as_mut().unwrap() };
392    // SAFETY: The kernel should have given us a valid `ucontext` here.
393    let uctx = unsafe { uctx.cast::<ucontext>().as_mut() };
394    // SAFETY: We can only assume that the signal handlers are sound.
395    unsafe { handle_hardware_error_signal_inner(prev_ctx.ctx(), signal, info, uctx) };
396}
397
398pub fn install_hardware_error_handlers() {
399    // SA_NODEFER: Don't block the current signal in the handler.
400    // Generating one of these signals while it is blocked is
401    // undefined behavior; the handler itself detects recursion.
402    // SA_SIGINFO: Required because we're specifying
403    // sa_sigaction.
404    // SA_ONSTACK: Use the alternate signal handling stack,
405    // to avoid interfering with userspace thread stacks.
406    let flags =
407        SigActionFlags::SA_SIGINFO | SigActionFlags::SA_NODEFER | SigActionFlags::SA_ONSTACK;
408    let handler = SignalHandler::Action(handle_hardware_error_signal);
409    let action = sigaction::new_with_default_restorer(handler, flags, sigset_t::EMPTY);
410    for signal in [
411        Signal::SIGSEGV,
412        Signal::SIGILL,
413        Signal::SIGBUS,
414        Signal::SIGFPE,
415    ] {
416        // SAFETY: We've set up a valid handler.
417        unsafe { linux_api::signal::rt_sigaction(signal, &action, None) }.unwrap();
418    }
419}
420
421mod export {
422    use super::*;
423
424    /// Handle pending unblocked signals, and return whether *all* corresponding
425    /// signal actions had the SA_RESTART flag set.
426    ///
427    /// `ucontext` will be passed through to handlers if non-NULL. This should
428    /// generally only be done if the caller has a `ucontext` that will be swapped to
429    /// after this code returns; e.g. one that was passed to our own signal handler,
430    /// which will be swapped to when that handler returns.
431    ///
432    /// If `ucontext` is NULL, one will be created at the point where we invoke
433    /// the handler, and swapped back to when it returns.
434    /// TODO: Creating `ucontext_t` is currently only implemented for handlers that
435    /// execute on a sigaltstack.
436    ///
437    /// # Safety
438    ///
439    /// `ucontext` must be dereferenceable if not NULL.
440    ///
441    /// Configured handlers for all pending unblocked signals must be safe to call. (Which
442    /// we basically can't ensure).
443    #[unsafe(no_mangle)]
444    pub unsafe extern "C-unwind" fn shim_process_signals(ucontext: *mut libc::ucontext_t) -> bool {
445        // `libc::ucontext_t` appears to be safe to cast to a kernel `ucontext`; as
446        // verified experimentally and by manual inspection of the definitions.
447        //
448        // The libc definition has some extra fields at the end, but we're
449        // careful not to copy the ucontext so they shouldn't hurt anything.
450        let ucontext: *mut ucontext = ucontext.cast();
451
452        // SAFETY: ensured by caller.
453        unsafe { process_signals(ucontext.as_mut()) }
454    }
455
456    /// Install signal handlers for signals that can be generated by hardware errors.
457    /// e.g. SIGSEGV
458    #[unsafe(no_mangle)]
459    pub unsafe extern "C-unwind" fn shim_install_hardware_error_handlers() {
460        install_hardware_error_handlers()
461    }
462
463    /// Handle a hardware error signal that was raised in `exe_ctx`.
464    ///
465    /// More-specialized error handlers (e.g. for rdtsc) can invoke this handler
466    /// directly when unable to handle the current signal (e.g. when a SIGSEGV wasn't
467    /// caused by an rdtsc instruction).
468    ///
469    /// # Safety
470    ///
471    /// `info` and `ctx` must be non-NULL and safely dereferenceable.
472    #[unsafe(no_mangle)]
473    pub unsafe extern "C-unwind" fn shim_handle_hardware_error_signal(
474        exe_ctx: ExecutionContext,
475        signo: i32,
476        info: *mut siginfo_t,
477        uctx: *mut linux_api::ucontext::linux_ucontext,
478    ) {
479        let signal = Signal::try_from(signo).unwrap();
480        // SAFETY: Caller ensures.
481        let info = unsafe { info.as_mut().unwrap() };
482        // SAFETY: Caller ensures.
483        let uctx = unsafe { uctx.as_mut() };
484        // SAFETY: We can only assume that the signal handlers are sound.
485        unsafe {
486            handle_hardware_error_signal_inner(exe_ctx, signal, info, uctx);
487        }
488    }
489}