lucet_runtime_internals/instance/
signals.rs1use crate::context::Context;
2use crate::error::Error;
3use crate::instance::{
4 siginfo_ext::SiginfoExt, FaultDetails, Instance, State, TerminationDetails, CURRENT_INSTANCE,
5 HOST_CTX,
6};
7use crate::sysdeps::UContextPtr;
8use lazy_static::lazy_static;
9use libc::{c_int, c_void, siginfo_t, SIGBUS, SIGSEGV};
10use lucet_module::TrapCode;
11use nix::sys::signal::{
12 pthread_sigmask, raise, sigaction, SaFlags, SigAction, SigHandler, SigSet, SigmaskHow, Signal,
13};
14use std::mem::MaybeUninit;
15use std::panic;
16use std::sync::{Arc, Mutex};
17
18lazy_static! {
19 static ref LUCET_SIGNAL_STATE: Mutex<Option<SignalState>> = Mutex::new(None);
21}
22
23pub enum SignalBehavior {
27 Default,
29 Continue,
31 Terminate,
33}
34
35pub type SignalHandler = dyn Fn(
36 &Instance,
37 &Option<TrapCode>,
38 libc::c_int,
39 *const siginfo_t,
40 *const c_void,
41) -> SignalBehavior;
42
43pub fn signal_handler_none(
44 _inst: &Instance,
45 _trapcode: &Option<TrapCode>,
46 _signum: libc::c_int,
47 _siginfo_ptr: *const siginfo_t,
48 _ucontext_ptr: *const c_void,
49) -> SignalBehavior {
50 SignalBehavior::Default
51}
52
53impl Instance {
54 pub(crate) fn with_signals_on<F, R>(&mut self, f: F) -> Result<R, Error>
55 where
56 F: FnOnce(&mut Instance) -> Result<R, Error>,
57 {
58 let guest_sigstack = SigStack::new(
62 self.alloc.slot().sigstack,
63 SigStackFlags::empty(),
64 self.alloc.slot().limits.signal_stack_size,
65 );
66 let previous_sigstack = unsafe { sigaltstack(Some(guest_sigstack)) }
67 .expect("enabling or changing the signal stack succeeds");
68 if let Some(previous_sigstack) = previous_sigstack {
69 assert!(
70 !previous_sigstack
71 .flags()
72 .contains(SigStackFlags::SS_ONSTACK),
73 "an instance was created with a signal stack"
74 );
75 }
76 let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
77 if let Some(ref mut state) = *ostate {
78 state.counter += 1;
79 } else {
80 unsafe {
81 setup_guest_signal_state(&mut ostate);
82 }
83 }
84 drop(ostate);
85
86 let res = f(self);
88
89 let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
90 let counter_zero = if let Some(ref mut state) = *ostate {
91 state.counter -= 1;
92 if state.counter == 0 {
93 unsafe {
94 restore_host_signal_state(state);
95 }
96 true
97 } else {
98 false
99 }
100 } else {
101 panic!("signal handlers weren't installed at instance exit");
102 };
103 if counter_zero {
104 *ostate = None;
105 }
106
107 unsafe {
108 if !altstack_flags()
110 .expect("the current stack flags could be retrieved")
111 .contains(SigStackFlags::SS_ONSTACK)
112 {
113 sigaltstack(previous_sigstack).expect("sigaltstack restoration succeeds");
114 }
115 }
116
117 res
118 }
119}
120
121extern "C" fn handle_signal(signum: c_int, siginfo_ptr: *mut siginfo_t, ucontext_ptr: *mut c_void) {
127 let signal = Signal::from_c_int(signum).expect("signum is a valid signal");
128 if !(signal == Signal::SIGBUS
129 || signal == Signal::SIGSEGV
130 || signal == Signal::SIGILL
131 || signal == Signal::SIGFPE
132 || signal == Signal::SIGALRM)
133 {
134 panic!("unexpected signal in guest signal handler: {:?}", signal);
135 }
136 assert!(!siginfo_ptr.is_null(), "siginfo must not be null");
137
138 assert!(!ucontext_ptr.is_null(), "ucontext_ptr must not be null");
141 let ctx = UContextPtr::new(ucontext_ptr);
142 let rip = ctx.get_ip();
143
144 let switch_to_host = CURRENT_INSTANCE.with(|current_instance| {
145 let mut current_instance = current_instance.borrow_mut();
146
147 if current_instance.is_none() {
148 unsafe {
152 reraise_host_signal_in_handler(signal, signum, siginfo_ptr, ucontext_ptr);
153 }
154 return false;
156 }
157
158 let inst = unsafe {
162 current_instance
163 .as_mut()
164 .expect("current instance exists")
165 .as_mut()
166 };
167
168 if signal == Signal::SIGALRM {
169 debug_assert!(!inst.kill_state.is_terminable());
176
177 inst.state = State::Terminating {
178 details: TerminationDetails::Remote,
179 };
180 return true;
181 }
182
183 let trapcode = inst.module.lookup_trapcode(rip);
184
185 let behavior = (inst.signal_handler)(inst, &trapcode, signum, siginfo_ptr, ucontext_ptr);
186 let switch_to_host = match behavior {
187 SignalBehavior::Continue => {
188 false
190 }
191 SignalBehavior::Terminate => {
192 inst.state = State::Terminating {
194 details: TerminationDetails::Signal,
195 };
196
197 true
198 }
199 SignalBehavior::Default => {
200 let mut thunk = || {
214 let siginfo = unsafe { *siginfo_ptr };
217 let rip_addr = rip as usize;
218 let unknown_fault = trapcode.is_none();
220
221 let outside_guard = (siginfo.si_signo == SIGSEGV || siginfo.si_signo == SIGBUS)
224 && inst
225 .alloc
226 .addr_location(siginfo.si_addr_ext())
227 .is_fault_fatal();
228
229 inst.state = State::Faulted {
231 details: FaultDetails {
232 fatal: unknown_fault || outside_guard,
233 trapcode: trapcode,
234 rip_addr,
235 rip_addr_details: None,
238 },
239 siginfo,
240 context: ctx.into(),
241 };
242 };
243
244 thunk();
245 true
246 }
247 };
248
249 if switch_to_host {
250 inst.kill_state.disable_termination();
252 }
253
254 switch_to_host
255 });
256
257 if switch_to_host {
258 HOST_CTX.with(|host_ctx| unsafe {
259 Context::set_from_signal(&*host_ctx.get())
260 .expect("can successfully switch back to the host context");
261 });
262 unreachable!()
263 }
264}
265
266struct SignalState {
267 counter: usize,
268 saved_sigbus: SigAction,
269 saved_sigfpe: SigAction,
270 saved_sigill: SigAction,
271 saved_sigsegv: SigAction,
272 saved_sigalrm: SigAction,
273 saved_panic_hook: Option<Arc<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>>>,
274}
275
276unsafe impl Send for SignalState {}
278
279unsafe fn setup_guest_signal_state(ostate: &mut Option<SignalState>) {
280 let mut masked_signals = SigSet::empty();
281 masked_signals.add(Signal::SIGBUS);
282 masked_signals.add(Signal::SIGFPE);
283 masked_signals.add(Signal::SIGILL);
284 masked_signals.add(Signal::SIGSEGV);
285 masked_signals.add(Signal::SIGALRM);
286
287 let sa = SigAction::new(
289 SigHandler::SigAction(handle_signal),
290 SaFlags::SA_RESTART | SaFlags::SA_SIGINFO | SaFlags::SA_ONSTACK,
291 masked_signals,
292 );
293 let saved_sigbus = sigaction(Signal::SIGBUS, &sa).expect("sigaction succeeds");
294 let saved_sigfpe = sigaction(Signal::SIGFPE, &sa).expect("sigaction succeeds");
295 let saved_sigill = sigaction(Signal::SIGILL, &sa).expect("sigaction succeeds");
296 let saved_sigsegv = sigaction(Signal::SIGSEGV, &sa).expect("sigaction succeeds");
297 let saved_sigalrm = sigaction(Signal::SIGALRM, &sa).expect("sigaction succeeds");
298
299 let saved_panic_hook = Some(setup_guest_panic_hook());
300
301 *ostate = Some(SignalState {
302 counter: 1,
303 saved_sigbus,
304 saved_sigfpe,
305 saved_sigill,
306 saved_sigsegv,
307 saved_sigalrm,
308 saved_panic_hook,
309 });
310}
311
312fn setup_guest_panic_hook() -> Arc<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> {
313 let saved_panic_hook = Arc::new(panic::take_hook());
314 let closure_saved_panic_hook = saved_panic_hook.clone();
315 std::panic::set_hook(Box::new(move |panic_info| {
316 if panic_info
317 .payload()
318 .downcast_ref::<TerminationDetails>()
319 .is_none()
320 {
321 closure_saved_panic_hook(panic_info);
322 } else {
323 }
327 }));
328 saved_panic_hook
329}
330
331unsafe fn restore_host_signal_state(state: &mut SignalState) {
332 sigaction(Signal::SIGBUS, &state.saved_sigbus).expect("sigaction succeeds");
334 sigaction(Signal::SIGFPE, &state.saved_sigfpe).expect("sigaction succeeds");
335 sigaction(Signal::SIGILL, &state.saved_sigill).expect("sigaction succeeds");
336 sigaction(Signal::SIGSEGV, &state.saved_sigsegv).expect("sigaction succeeds");
337 sigaction(Signal::SIGALRM, &state.saved_sigalrm).expect("sigaction succeeds");
338
339 drop(panic::take_hook());
341 state
342 .saved_panic_hook
343 .take()
344 .map(|hook| Arc::try_unwrap(hook).map(|hook| panic::set_hook(hook)));
345}
346
347unsafe fn reraise_host_signal_in_handler(
348 sig: Signal,
349 signum: libc::c_int,
350 siginfo_ptr: *mut libc::siginfo_t,
351 ucontext_ptr: *mut c_void,
352) {
353 let saved_handler = {
354 if let Some(ref state) = *LUCET_SIGNAL_STATE.lock().unwrap() {
357 match sig {
358 Signal::SIGBUS => state.saved_sigbus.clone(),
359 Signal::SIGFPE => state.saved_sigfpe.clone(),
360 Signal::SIGILL => state.saved_sigill.clone(),
361 Signal::SIGSEGV => state.saved_sigsegv.clone(),
362 Signal::SIGALRM => state.saved_sigalrm.clone(),
363 sig => panic!(
364 "unexpected signal in reraise_host_signal_in_handler: {:?}",
365 sig
366 ),
367 }
368 } else {
369 let mut unmask = SigSet::empty();
378 unmask.add(sig);
379 pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
380 .expect("pthread_sigmask succeeds");
381 raise(sig).expect("raise succeeds");
383 return;
384 }
385 };
386
387 match saved_handler.handler() {
388 SigHandler::SigDfl => {
389 sigaction(sig, &saved_handler).expect("sigaction succeeds");
392 let mut unmask = SigSet::empty();
393 unmask.add(sig);
394 pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&unmask), None)
395 .expect("pthread_sigmask succeeds");
396 raise(sig).expect("raise succeeds");
397 }
398 SigHandler::SigIgn => {
399 return;
403 }
404 SigHandler::Handler(f) => {
405 f(signum)
407 }
408 SigHandler::SigAction(f) => {
409 f(signum, siginfo_ptr, ucontext_ptr)
411 }
412 }
413}
414
415use bitflags::bitflags;
420
421#[derive(Copy, Clone)]
422pub struct SigStack {
423 stack: libc::stack_t,
424}
425
426impl SigStack {
427 pub fn new(sp: *mut libc::c_void, flags: SigStackFlags, size: libc::size_t) -> SigStack {
428 let stack = libc::stack_t {
429 ss_sp: sp,
430 ss_flags: flags.bits(),
431 ss_size: size,
432 };
433 SigStack { stack }
434 }
435
436 pub fn disabled() -> SigStack {
437 let stack = libc::stack_t {
438 ss_sp: std::ptr::null_mut(),
439 ss_flags: SigStackFlags::SS_DISABLE.bits(),
440 ss_size: libc::SIGSTKSZ,
441 };
442 SigStack { stack }
443 }
444
445 pub fn flags(&self) -> SigStackFlags {
446 SigStackFlags::from_bits_truncate(self.stack.ss_flags)
447 }
448}
449
450impl AsRef<libc::stack_t> for SigStack {
451 fn as_ref(&self) -> &libc::stack_t {
452 &self.stack
453 }
454}
455
456impl AsMut<libc::stack_t> for SigStack {
457 fn as_mut(&mut self) -> &mut libc::stack_t {
458 &mut self.stack
459 }
460}
461
462bitflags! {
463 pub struct SigStackFlags: libc::c_int {
464 const SS_ONSTACK = libc::SS_ONSTACK;
465 const SS_DISABLE = libc::SS_DISABLE;
466 }
467}
468
469pub unsafe fn sigaltstack(new_sigstack: Option<SigStack>) -> nix::Result<Option<SigStack>> {
470 let mut previous_stack = MaybeUninit::<libc::stack_t>::uninit();
471 let disabled_sigstack = SigStack::disabled();
472 let new_stack = match new_sigstack {
473 None => &disabled_sigstack.stack,
474 Some(ref new_stack) => &new_stack.stack,
475 };
476 let res = libc::sigaltstack(
477 new_stack as *const libc::stack_t,
478 previous_stack.as_mut_ptr(),
479 );
480 nix::errno::Errno::result(res).map(|_| {
481 let sigstack = SigStack {
482 stack: previous_stack.assume_init(),
483 };
484 if sigstack.flags().contains(SigStackFlags::SS_DISABLE) {
485 None
486 } else {
487 Some(sigstack)
488 }
489 })
490}
491
492pub unsafe fn altstack_flags() -> nix::Result<SigStackFlags> {
493 let mut current_stack = MaybeUninit::<libc::stack_t>::uninit();
494 let res = libc::sigaltstack(std::ptr::null_mut(), current_stack.as_mut_ptr());
495 nix::errno::Errno::result(res)
496 .map(|_| SigStackFlags::from_bits_truncate(current_stack.assume_init().ss_flags))
497}