Add the sigaltstack system call

This commit is contained in:
LI Qing 2020-05-11 12:06:24 +00:00 committed by Tate, Hongliang Tian
parent 255f277f30
commit 2d34ee349e
9 changed files with 272 additions and 10 deletions

@ -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();

@ -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<SigQueues>,
sig_mask: SgxRwLock<SigSet>,
sig_tmp_mask: SgxRwLock<SigSet>,
sig_stack: SgxMutex<Option<SigStack>>,
}
#[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<Option<SigStack>> {
&self.sig_stack
}
/// Get a file from the file table.
pub fn file(&self, fd: FileDesc) -> Result<FileRef> {
self.files().lock().unwrap().get(fd)

@ -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<SigStack>, curr_user_ctxt: &CpuContext) -> Result<SigStack> {
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)
}

@ -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 = {
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;
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;
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)? }
};

@ -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;

@ -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<Self> {
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<Self> {
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,
}
}
}

@ -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<isize
*buf = pending.to_c();
Ok(0)
}
pub fn do_sigaltstack(
new_ss_c: *const stack_t,
old_ss_c: *mut stack_t,
user_context: *const CpuContext,
) -> Result<isize> {
// 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)
}

@ -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)

@ -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[]) {