Add the sigaltstack system call
This commit is contained in:
parent
255f277f30
commit
2d34ee349e
@ -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)
|
||||
|
43
src/libos/src/signal/do_sigaltstack.rs
Normal file
43
src/libos/src/signal/do_sigaltstack.rs
Normal file
@ -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;
|
||||
|
||||
|
84
src/libos/src/signal/sig_stack.rs
Normal file
84
src/libos/src/signal/sig_stack.rs
Normal file
@ -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[]) {
|
||||
|
Loading…
Reference in New Issue
Block a user