| // 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); |
| } |