blob: 96dd8fe0ad757cdd49ca85cef538e0eed656baa5 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
use crate::sgx_types::{sgx_enclave_id_t, sgx_status_t};
use libc::{self, c_int, c_void, sigaction, siginfo_t, sigset_t};
use libc::{SA_SIGINFO, SIG_DFL, SIG_ERR, SIG_SETMASK};
use std::collections::HashMap;
use std::io::Error;
use std::mem;
use std::sync::{Mutex, Once};
static DISPATCHER_INIT: Once = Once::new();
static mut GLOBAL_DATA: Option<GlobalData> = None;
#[allow(dead_code)]
const SIGRTMIN: c_int = 32;
const SIGRTMAX: c_int = 64;
const NSIG: c_int = SIGRTMAX + 1;
#[linkage = "weak"]
#[no_mangle]
extern "C" fn t_signal_handler_ecall(
_eid: sgx_enclave_id_t,
_retval: *mut c_int,
_info: *const siginfo_t,
) -> sgx_status_t {
sgx_status_t::SGX_ERROR_UNEXPECTED
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SigNum(i32);
impl SigNum {
pub fn from_raw(signo: i32) -> Option<SigNum> {
if signo <= 0 || signo >= NSIG {
None
} else {
Some(SigNum(signo))
}
}
pub unsafe fn from_raw_uncheck(signo: i32) -> SigNum {
SigNum(signo)
}
pub fn raw(&self) -> i32 {
self.0
}
}
#[derive(Copy, Clone)]
pub struct SigSet(sigset_t);
impl SigSet {
pub fn new() -> SigSet {
let set = unsafe {
let mut set: sigset_t = mem::zeroed();
libc::sigemptyset(&mut set as *mut sigset_t);
set
};
SigSet(set)
}
pub fn fill(&mut self) {
unsafe {
libc::sigfillset(&mut self.0 as *mut sigset_t);
}
}
pub unsafe fn from_raw(set: sigset_t) -> SigSet {
SigSet(set)
}
pub fn raw(&self) -> sigset_t {
self.0
}
}
struct GlobalData {
signal_dispatcher: SignalDispatcher,
}
impl GlobalData {
fn get() -> &'static GlobalData {
unsafe { GLOBAL_DATA.as_ref().unwrap() }
}
fn ensure() -> &'static GlobalData {
DISPATCHER_INIT.call_once(|| unsafe {
GLOBAL_DATA = Some(GlobalData {
signal_dispatcher: SignalDispatcher::new(),
});
});
Self::get()
}
}
struct SignalDispatcher {
signal_set: Mutex<HashMap<SigNum, sgx_enclave_id_t>>,
}
impl SignalDispatcher {
fn new() -> SignalDispatcher {
SignalDispatcher {
signal_set: Mutex::new(HashMap::new()),
}
}
fn register_signal(
&self,
signo: SigNum,
enclave_id: sgx_enclave_id_t,
) -> Option<sgx_enclave_id_t> {
// Block all signals when registering a signal handler to avoid deadlock.
let mut mask = SigSet::new();
let oldmask = SigSet::new();
mask.fill();
unsafe {
libc::sigprocmask(
SIG_SETMASK,
&mask.raw(),
&mut oldmask.raw() as *mut sigset_t,
);
}
let old = self.signal_set.lock().unwrap().insert(signo, enclave_id);
unsafe {
libc::sigprocmask(SIG_SETMASK, &oldmask.raw(), 0 as *mut sigset_t);
}
old
}
fn get_eid_for_signal(&self, signo: SigNum) -> Option<sgx_enclave_id_t> {
self.signal_set.lock().unwrap().get(&signo).copied()
}
fn deregister_all_signals_for_eid(&self, eid: sgx_enclave_id_t) {
let mut mask = SigSet::new();
let oldmask = SigSet::new();
mask.fill();
unsafe {
libc::sigprocmask(
SIG_SETMASK,
&mask.raw(),
&mut oldmask.raw() as *mut sigset_t,
);
}
// If this enclave has registered any signals, deregister them and set the
// signal handler to the default one.
self.signal_set.lock().unwrap().retain(|&signum, &mut v| {
if v == eid {
unsafe { if libc::signal(signum.raw(), SIG_DFL) == SIG_ERR {} }
}
v != eid
});
unsafe {
libc::sigprocmask(SIG_SETMASK, &oldmask.raw(), 0 as *mut sigset_t);
}
}
unsafe fn handle_signal(
&self,
signo: SigNum,
info: &siginfo_t,
_context: *const c_void,
) -> c_int {
let mut retval: c_int = 0;
let eid = match self.get_eid_for_signal(signo) {
Some(eid) => eid,
None => return -1,
};
let result = t_signal_handler_ecall(
eid,
&mut retval as *mut c_int,
info as *const siginfo_t,
);
if result != sgx_status_t::SGX_SUCCESS {
return -1;
}
retval
}
}
pub fn deregister_all_signals_for_eid(enclave_id: sgx_enclave_id_t) {
let global = GlobalData::ensure();
global
.signal_dispatcher
.deregister_all_signals_for_eid(enclave_id);
}
#[no_mangle]
pub extern "C" fn u_sigaction_ocall(
error: *mut c_int,
signum: c_int,
act: *const sigaction,
oldact: *mut sigaction,
enclave_id: sgx_enclave_id_t,
) -> c_int {
let mut errno = 0;
let signo = SigNum::from_raw(signum);
if signo.is_none() || act.is_null() {
if !error.is_null() {
unsafe {
*error = libc::EINVAL;
}
}
return -1;
}
let e_act = unsafe { &*act };
let ret = if e_act.sa_sigaction == 0 {
let global = GlobalData::ensure();
global
.signal_dispatcher
.register_signal(signo.unwrap(), enclave_id);
type FnSaSigaction = extern "C" fn(c_int, *const siginfo_t, *const c_void);
let new_act = sigaction {
sa_sigaction: unsafe { mem::transmute::<FnSaSigaction, usize>(handle_signal_entry) },
// Set the flag so that sa_sigaction is registered as the signal handler
// instead of sa_handler.
sa_flags: e_act.sa_flags | SA_SIGINFO,
sa_mask: e_act.sa_mask,
sa_restorer: None,
};
let mut old_act: sigaction = unsafe { mem::zeroed() };
unsafe { libc::sigaction(signum, &new_act, &mut old_act as *mut sigaction) }
} else {
unsafe { libc::sigaction(signum, act as *const sigaction, oldact) }
};
if ret < 0 {
errno = Error::last_os_error().raw_os_error().unwrap_or(0);
}
if !error.is_null() {
unsafe {
*error = errno;
}
}
extern "C" fn handle_signal_entry(
signum: c_int,
info: *const siginfo_t,
ucontext: *const c_void,
) {
let signo = SigNum::from_raw(signum);
if info.is_null() || signo.is_none() {
return;
}
unsafe {
GlobalData::get()
.signal_dispatcher
.handle_signal(signo.unwrap(), &(*info), ucontext);
}
}
ret
}
#[no_mangle]
pub extern "C" fn u_sigprocmask_ocall(
error: *mut c_int,
signum: c_int,
set: *const sigset_t,
oldset: *mut sigset_t,
) -> c_int {
let mut errno = 0;
let ret = unsafe { libc::sigprocmask(signum, set, oldset) };
if ret < 0 {
errno = Error::last_os_error().raw_os_error().unwrap_or(0);
}
if !error.is_null() {
unsafe {
*error = errno;
}
}
ret
}
#[no_mangle]
pub extern "C" fn u_raise_ocall(signum: c_int) -> c_int {
unsafe { libc::raise(signum) }
}
#[no_mangle]
pub extern "C" fn u_signal_clear_ocall(eid: sgx_enclave_id_t) {
deregister_all_signals_for_eid(eid);
}