Improve sigaltstack(2) usage in lucet-runtime
Make the `sigaltstack(2)` wrapper more Rustic, and compatible
with macOS, where providing the stack size is required, even with
`SS_DISABLE`.
@@ -59,9 +59,16 @@ impl Instance {
SigStackFlags::empty(),
libc::SIGSTKSZ,
);
- let saved_sigstack =
- unsafe { sigaltstack(&guest_sigstack).expect("saving sigaltstack succeeds") };
-
+ let previous_sigstack = unsafe { sigaltstack(Some(guest_sigstack)) }
+ .expect("enabling or changing the signal stack succeeds");
+ if let Some(previous_sigstack) = previous_sigstack {
+ assert!(
+ !previous_sigstack
+ .flags()
+ .contains(SigStackFlags::SS_ONSTACK),
+ "an instance was created with a signal stack"
+ );
+ }
let mut ostate = LUCET_SIGNAL_STATE.lock().unwrap();
if let Some(ref mut state) = *ostate {
state.counter += 1;
@@ -81,7 +88,12 @@ impl Instance {
if state.counter == 0 {
unsafe {
// restore the host signal stack
- sigaltstack(&saved_sigstack).expect("sigaltstack restoration succeeds");
+ if !altstack_flags()
+ .expect("the current stack flags could be retrieved")
+ .contains(SigStackFlags::SS_ONSTACK)
+ {
+ sigaltstack(previous_sigstack).expect("sigaltstack restoration succeeds");
+ }
restore_host_signal_state(state);
}
true
@@ -333,6 +345,14 @@ impl SigStack {
SigStack { stack }
}
+ pub fn disabled() -> SigStack {
+ let mut stack = unsafe { std::mem::uninitialized::<libc::stack_t>() };
+ stack.ss_sp = std::ptr::null_mut();
+ stack.ss_flags = SigStackFlags::SS_DISABLE.bits();
+ stack.ss_size = libc::SIGSTKSZ;
+ SigStack { stack }
+ }
+
pub fn flags(&self) -> SigStackFlags {
SigStackFlags::from_bits_truncate(self.stack.ss_flags)
}
@@ -357,13 +377,35 @@ bitflags! {
}
}
-pub unsafe fn sigaltstack(ss: &SigStack) -> nix::Result<SigStack> {
- let mut oldstack = std::mem::uninitialized::<libc::stack_t>();
-
+pub unsafe fn sigaltstack(new_sigstack: Option<SigStack>) -> nix::Result<Option<SigStack>> {
+ let mut previous_stack = std::mem::uninitialized::<libc::stack_t>();
+ let disabled_sigstack = SigStack::disabled();
+ let new_stack = match new_sigstack {
+ None => &disabled_sigstack.stack,
+ Some(ref new_stack) => &new_stack.stack,
+ };
let res = libc::sigaltstack(
- &ss.stack as *const libc::stack_t,
- &mut oldstack as *mut libc::stack_t,
+ new_stack as *const libc::stack_t,
+ &mut previous_stack as *mut libc::stack_t,
);
+ nix::errno::Errno::result(res).map(|_| {
+ let sigstack = SigStack {
+ stack: previous_stack,
+ };
+ if sigstack.flags().contains(SigStackFlags::SS_DISABLE) {
+ None
+ } else {
+ Some(sigstack)
+ }
+ })
+}
- nix::errno::Errno::result(res).map(|_| SigStack { stack: oldstack })
+pub unsafe fn altstack_flags() -> nix::Result<SigStackFlags> {
+ let mut current_stack = std::mem::uninitialized::<libc::stack_t>();
+ let res = libc::sigaltstack(
+ std::ptr::null_mut(),
+ &mut current_stack as *mut libc::stack_t,
+ );
+ nix::errno::Errno::result(res)
+ .map(|_| SigStackFlags::from_bits_truncate(current_stack.ss_flags))
}
