From 2d34ee349e6676f03e0cf60589191b4332848f64 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Mon, 11 May 2020 12:06:24 +0000 Subject: [PATCH] Add the sigaltstack system call --- src/libos/src/process/thread/builder.rs | 2 + src/libos/src/process/thread/mod.rs | 8 ++- src/libos/src/signal/do_sigaltstack.rs | 43 +++++++++++++ src/libos/src/signal/do_sigreturn.rs | 25 ++++++-- src/libos/src/signal/mod.rs | 5 +- src/libos/src/signal/sig_stack.rs | 84 +++++++++++++++++++++++++ src/libos/src/signal/syscalls.rs | 40 +++++++++++- src/libos/src/syscall/mod.rs | 10 ++- test/signal/main.c | 65 +++++++++++++++++++ 9 files changed, 272 insertions(+), 10 deletions(-) create mode 100644 src/libos/src/signal/do_sigaltstack.rs create mode 100644 src/libos/src/signal/sig_stack.rs diff --git a/src/libos/src/process/thread/builder.rs b/src/libos/src/process/thread/builder.rs index 92622dc6..3d088349 100644 --- a/src/libos/src/process/thread/builder.rs +++ b/src/libos/src/process/thread/builder.rs @@ -101,6 +101,7 @@ impl ThreadBuilder { let sig_queues = SgxMutex::new(SigQueues::new()); let sig_mask = SgxRwLock::new(SigSet::new_empty()); let sig_tmp_mask = SgxRwLock::new(SigSet::new_empty()); + let sig_stack = SgxMutex::new(None); let new_thread = Arc::new(Thread { task, @@ -116,6 +117,7 @@ impl ThreadBuilder { sig_queues, sig_mask, sig_tmp_mask, + sig_stack, }); let mut inner = new_thread.process().inner(); diff --git a/src/libos/src/process/thread/mod.rs b/src/libos/src/process/thread/mod.rs index 4daf1272..eb2ec56b 100644 --- a/src/libos/src/process/thread/mod.rs +++ b/src/libos/src/process/thread/mod.rs @@ -7,7 +7,7 @@ use super::{ TermStatus, ThreadRef, }; use crate::prelude::*; -use crate::signal::{SigQueues, SigSet}; +use crate::signal::{SigQueues, SigSet, SigStack}; pub use self::builder::ThreadBuilder; pub use self::id::ThreadId; @@ -35,6 +35,7 @@ pub struct Thread { sig_queues: SgxMutex, sig_mask: SgxRwLock, sig_tmp_mask: SgxRwLock, + sig_stack: SgxMutex>, } #[derive(Debug, PartialEq, Clone, Copy)] @@ -91,6 +92,11 @@ impl Thread { &self.sig_tmp_mask } + /// Get the alternate signal stack. + pub fn sig_stack(&self) -> &SgxMutex> { + &self.sig_stack + } + /// Get a file from the file table. pub fn file(&self, fd: FileDesc) -> Result { self.files().lock().unwrap().get(fd) diff --git a/src/libos/src/signal/do_sigaltstack.rs b/src/libos/src/signal/do_sigaltstack.rs new file mode 100644 index 00000000..91f47277 --- /dev/null +++ b/src/libos/src/signal/do_sigaltstack.rs @@ -0,0 +1,43 @@ +use super::sig_stack::{SigStack, SigStackFlags, MINSIGSTKSZ}; +use crate::prelude::*; +use crate::syscall::CpuContext; + +pub fn do_sigaltstack(new_ss: &Option, curr_user_ctxt: &CpuContext) -> Result { + debug!("do_sigaltstack: new_ss:{:?}", new_ss); + let thread = current!(); + let mut sig_stack = thread.sig_stack().lock().unwrap(); + let old_ss = if let Some(sig_stack) = *sig_stack { + // Deny to update the stack when we are on the stack + if new_ss.is_some() && sig_stack.contains(curr_user_ctxt.rsp as usize) { + return_errno!(EPERM, "thread is on signal stack currently"); + } + + // Retrieve the old signal stack information + let flags = if sig_stack.contains(curr_user_ctxt.rsp as usize) { + SigStackFlags::SS_ONSTACK + } else { + SigStackFlags::EMPTY + }; + let mut ss: SigStack = Default::default(); + ss.update(sig_stack.sp(), flags, sig_stack.size()); + ss + } else { + Default::default() + }; + + // Set or update the signal stack if necessary + if let Some(new_ss) = new_ss { + *sig_stack = if new_ss.flags() == SigStackFlags::SS_DISABLE { + None + } else { + if new_ss.size() < MINSIGSTKSZ { + return_errno!(ENOMEM, "the new alternate signal stack is too small"); + } + if (new_ss.flags() == SigStackFlags::SS_AUTODISARM) { + warn!("The SS_AUTODISARM flag is not supported yet"); + } + Some(*new_ss) + }; + } + Ok(old_ss) +} diff --git a/src/libos/src/signal/do_sigreturn.rs b/src/libos/src/signal/do_sigreturn.rs index f9bd9853..beda0224 100644 --- a/src/libos/src/signal/do_sigreturn.rs +++ b/src/libos/src/signal/do_sigreturn.rs @@ -1,5 +1,6 @@ use super::c_types::{mcontext_t, siginfo_t, ucontext_t}; use super::constants::SIGKILL; +use super::sig_stack::SigStackFlags; use super::{SigAction, SigActionFlags, SigDefaultAction, SigSet, Signal}; use crate::prelude::*; use crate::process::{ProcessRef, TermStatus, ThreadRef}; @@ -176,10 +177,26 @@ fn handle_signals_by_user( ) -> Result<()> { // Represent the user stack in a memory safe way let mut user_stack = { - const BIG_ENOUGH_GAP: u64 = 1024; - const BIG_ENOUGH_SIZE: u64 = 4096; - let stack_top = (curr_user_ctxt.rsp - BIG_ENOUGH_GAP) as usize; - let stack_size = BIG_ENOUGH_SIZE as usize; + let get_stack_top = || -> usize { + if flags.contains(SigActionFlags::SA_ONSTACK) { + let thread = current!(); + let sig_stack = thread.sig_stack().lock().unwrap(); + if let Some(stack) = *sig_stack { + if !stack.contains(curr_user_ctxt.rsp as usize) { + let stack_top = stack.sp() + stack.size(); + return stack_top; + } + } + } + const BIG_ENOUGH_GAP: u64 = 1024; + let stack_top = (curr_user_ctxt.rsp - BIG_ENOUGH_GAP) as usize; + stack_top + }; + let stack_top = get_stack_top(); + let stack_size = { + const BIG_ENOUGH_SIZE: u64 = 4096; + BIG_ENOUGH_SIZE as usize + }; // TODO: validate the memory range of the stack unsafe { Stack::new(stack_top, stack_size)? } }; diff --git a/src/libos/src/signal/mod.rs b/src/libos/src/signal/mod.rs index f119fb61..62a6fdf6 100644 --- a/src/libos/src/signal/mod.rs +++ b/src/libos/src/signal/mod.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use sig_action::{SigAction, SigActionFlags, SigDefaultAction}; -pub use self::c_types::{sigaction_t, sigset_t}; +pub use self::c_types::{sigaction_t, sigset_t, stack_t}; pub use self::constants::*; pub use self::do_kill::do_kill_from_outside_enclave; pub use self::do_sigreturn::{deliver_signal, force_signal}; @@ -12,12 +12,14 @@ pub use self::sig_dispositions::SigDispositions; pub use self::sig_num::SigNum; pub use self::sig_queues::SigQueues; pub use self::sig_set::SigSet; +pub use self::sig_stack::SigStack; pub use self::signals::{FaultSignal, KernelSignal, Signal, UserSignal, UserSignalKind}; pub use self::syscalls::*; mod c_types; mod do_kill; mod do_sigaction; +mod do_sigaltstack; mod do_sigpending; mod do_sigprocmask; mod do_sigreturn; @@ -26,6 +28,7 @@ mod sig_dispositions; mod sig_num; mod sig_queues; mod sig_set; +mod sig_stack; mod signals; mod syscalls; diff --git a/src/libos/src/signal/sig_stack.rs b/src/libos/src/signal/sig_stack.rs new file mode 100644 index 00000000..e9843c56 --- /dev/null +++ b/src/libos/src/signal/sig_stack.rs @@ -0,0 +1,84 @@ +use super::c_types::stack_t; +use crate::prelude::*; + +pub const MINSIGSTKSZ: usize = 2048; + +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u32)] +pub enum SigStackFlags { + /// The alternate signal stack is enabled and the process is not executing on it + EMPTY = 0, + /// The process is currently executing on the alternate signal stack + SS_ONSTACK = 1, + /// The alternate signal stack is currently disabled + SS_DISABLE = 2, + /// The alternate signal stack has been marked to be autodisarmed + SS_AUTODISARM = 1 << 31, +} + +impl SigStackFlags { + pub fn from_u32(bits: u32) -> Result { + if bits > Self::SS_DISABLE as u32 && bits != Self::SS_AUTODISARM as u32 { + return_errno!(EINVAL, "invalid bits for sig stack flags"); + } + Ok(unsafe { core::mem::transmute(bits as u32) }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SigStack { + sp: usize, + flags: SigStackFlags, + size: usize, +} + +impl SigStack { + pub fn from_c(ss_c: &stack_t) -> Result { + Ok(Self { + sp: ss_c.ss_sp as usize, + flags: SigStackFlags::from_u32(ss_c.ss_flags as u32)?, + size: ss_c.ss_size as usize, + }) + } + + pub fn to_c(&self) -> stack_t { + stack_t { + ss_sp: self.sp as *mut c_void, + ss_flags: self.flags as i32, + ss_size: self.size, + } + } + + pub fn sp(&self) -> usize { + self.sp + } + + pub fn flags(&self) -> SigStackFlags { + self.flags + } + + pub fn size(&self) -> usize { + self.size + } + + pub fn update(&mut self, sp: usize, flags: SigStackFlags, size: usize) { + self.sp = sp; + self.flags = flags; + self.size = size; + } + + pub fn contains(&self, addr: usize) -> bool { + addr >= self.sp && addr - self.sp < self.size + } +} + +impl Default for SigStack { + fn default() -> Self { + Self { + sp: 0, + flags: SigStackFlags::SS_DISABLE, + size: 0, + } + } +} diff --git a/src/libos/src/signal/syscalls.rs b/src/libos/src/signal/syscalls.rs index 4ce7503b..a5b0bad0 100644 --- a/src/libos/src/signal/syscalls.rs +++ b/src/libos/src/signal/syscalls.rs @@ -1,10 +1,11 @@ use super::constants::*; use super::do_sigprocmask::MaskOp; use super::signals::FaultSignal; -use super::{sigaction_t, sigset_t, SigAction, SigNum, SigSet}; +use super::{sigaction_t, sigset_t, stack_t, SigAction, SigNum, SigSet, SigStack}; use crate::prelude::*; use crate::process::ProcessFilter; use crate::syscall::CpuContext; +use crate::util::mem_util::from_user; pub fn do_rt_sigaction( signum_c: c_int, @@ -119,3 +120,40 @@ pub fn do_rt_sigpending(buf_ptr: *mut sigset_t, buf_size: usize) -> Result Result { + // C types -> Rust types + let new_ss = { + if !new_ss_c.is_null() { + from_user::check_ptr(new_ss_c)?; + let new_ss_c = unsafe { &*new_ss_c }; + let new_ss = SigStack::from_c(new_ss_c)?; + Some(new_ss) + } else { + None + } + }; + let mut old_ss_c = { + if !old_ss_c.is_null() { + from_user::check_mut_ptr(old_ss_c)?; + let old_ss_c = unsafe { &mut *old_ss_c }; + Some(old_ss_c) + } else { + None + } + }; + let user_context = unsafe { &*user_context }; + + // Do sigaltstack + let old_ss = super::do_sigaltstack::do_sigaltstack(&new_ss, user_context)?; + + // Retrieve old signal stack, if needed + if let Some(old_ss_c) = old_ss_c { + *old_ss_c = old_ss.to_c(); + } + Ok(0) +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 60cbe862..624dabf9 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -38,8 +38,8 @@ use crate::process::{ }; use crate::sched::{do_sched_getaffinity, do_sched_setaffinity, do_sched_yield}; use crate::signal::{ - do_kill, do_rt_sigaction, do_rt_sigpending, do_rt_sigprocmask, do_rt_sigreturn, do_tgkill, - do_tkill, sigaction_t, sigset_t, + do_kill, do_rt_sigaction, do_rt_sigpending, do_rt_sigprocmask, do_rt_sigreturn, do_sigaltstack, + do_tgkill, do_tkill, sigaction_t, sigset_t, stack_t, }; use crate::vm::{MMapFlags, VMPerms}; use crate::{fs, process, std, vm}; @@ -206,7 +206,7 @@ macro_rules! process_syscall_table_with_callback { (RtSigtimedwait = 128) => handle_unsupported(), (RtSigqueueinfo = 129) => handle_unsupported(), (RtSigsuspend = 130) => handle_unsupported(), - (Sigaltstack = 131) => handle_unsupported(), + (Sigaltstack = 131) => do_sigaltstack(ss: *const stack_t, old_ss: *mut stack_t, context: *const CpuContext), (Utime = 132) => handle_unsupported(), (Mknod = 133) => handle_unsupported(), (Uselib = 134) => handle_unsupported(), @@ -626,6 +626,10 @@ fn do_syscall(user_context: &mut CpuContext) { } else if syscall.num == SyscallNum::HandleException { // syscall.args[0] == info syscall.args[1] = user_context as *mut _ as isize; + } else if syscall.num == SyscallNum::Sigaltstack { + // syscall.args[0] == new_ss + // syscall.args[1] == old_ss + syscall.args[2] = user_context as *const _ as isize; } dispatch_syscall(syscall) diff --git a/test/signal/main.c b/test/signal/main.c index c14fcb94..e5f74fc5 100644 --- a/test/signal/main.c +++ b/test/signal/main.c @@ -260,6 +260,70 @@ int test_catch_fault() { #endif /* SGX_MODE_SIM */ } +// ============================================================================ +// Test handle signal on alternate signal stack +// ============================================================================ + +#define MAX_ALTSTACK_RECURSION_LEVEL 2 + +stack_t g_old_ss; + +static void handle_sigpipe(int num, siginfo_t* info, void* context) { + static volatile int recursion_level = 0; + printf("Hello from SIGPIPE signal handler on the alternate signal stack (recursion_level = %d)\n", + recursion_level); + + // save old_ss to check if we are on stack + stack_t old_ss; + sigaltstack(NULL, &old_ss); + g_old_ss = old_ss; + + recursion_level++; + if (recursion_level <= MAX_ALTSTACK_RECURSION_LEVEL) + raise(SIGPIPE); + recursion_level--; +} + +int test_sigaltstack() { + static char stack[SIGSTKSZ]; + stack_t expected_ss = { + .ss_size = SIGSTKSZ, + .ss_sp = stack, + .ss_flags = 0, + }; + if (sigaltstack(&expected_ss, NULL) < 0) { + THROW_ERROR("failed to call sigaltstack"); + } + stack_t actual_ss; + if (sigaltstack(NULL, &actual_ss) < 0) { + THROW_ERROR("failed to call sigaltstack"); + } + if (actual_ss.ss_size != expected_ss.ss_size + || actual_ss.ss_sp != expected_ss.ss_sp + || actual_ss.ss_flags != expected_ss.ss_flags) { + THROW_ERROR("failed to check the signal stack after set"); + } + + struct sigaction new_action, old_action; + new_action.sa_sigaction = handle_sigpipe; + new_action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; + if (sigaction(SIGPIPE, &new_action, &old_action) < 0) { + THROW_ERROR("registering new signal handler failed"); + } + if (old_action.sa_handler != SIG_DFL) { + THROW_ERROR("unexpected old sig handler"); + } + + raise(SIGPIPE); + if (g_old_ss.ss_flags != SS_ONSTACK) { + THROW_ERROR("check stack flags failed"); + } + + if (sigaction(SIGPIPE, &old_action, NULL) < 0) { + THROW_ERROR("restoring old signal handler failed"); + } + return 0; +} // ============================================================================ // Test suite main @@ -271,6 +335,7 @@ static test_case_t test_cases[] = { TEST_CASE(test_abort), TEST_CASE(test_kill), TEST_CASE(test_catch_fault), + TEST_CASE(test_sigaltstack), }; int main(int argc, const char* argv[]) {