diff --git a/src/Enclave.edl b/src/Enclave.edl index 50b9f63b..30b43994 100644 --- a/src/Enclave.edl +++ b/src/Enclave.edl @@ -61,6 +61,23 @@ enclave { * ESRCH - Cannot find the LibOS thread. */ public int occlum_ecall_exec_thread(int libos_tid, int host_tid); + + /* + * Send a signal to one or multiple LibOS processes. + * + * If pid == -1, send the signal to all processes. If pid > 0, send + * the signal to the specific process. For the purpose of security, + * the only allowed signals for now are SIGKILL and SIGTERM. + * + * @retval On success, return 0. On error, return -errno. + * + * The possible values of errno are + * EAGAIN - The LibOS is not initialized. + * EINVAL - The value of an argument are invalid. + * ESRCH - Cannot find the process specified by pid. + * EPERM - No permission to send the signal or to the process. + */ + public int occlum_ecall_kill(int pid, int sig); }; untrusted { diff --git a/src/libos/src/entry.rs b/src/libos/src/entry.rs index a6a0cdf1..db48cf1c 100644 --- a/src/libos/src/entry.rs +++ b/src/libos/src/entry.rs @@ -1,14 +1,16 @@ -use super::*; -use exception::*; -use fs::HostStdioFds; -use process::pid_t; use std::ffi::{CStr, CString, OsString}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; -use util::log::LevelFilter; -use util::mem_util::from_untrusted::*; -use util::sgx::allow_debug as sgx_allow_debug; + +use super::*; +use crate::exception::*; +use crate::fs::HostStdioFds; +use crate::process::ProcessFilter; +use crate::signal::SigNum; +use crate::util::log::LevelFilter; +use crate::util::mem_util::from_untrusted::*; +use crate::util::sgx::allow_debug as sgx_allow_debug; use sgx_tse::*; pub static mut INSTANCE_DIR: String = String::new(); @@ -53,7 +55,7 @@ pub extern "C" fn occlum_ecall_init(log_level: *const c_char, instance_dir: *con INIT_ONCE.call_once(|| { // Init the log infrastructure first so that log messages will be printed afterwards util::log::init(log_level); - + // Init MPX for SFI if MPX is available let report = rsgx_self_report(); if (report.body.attributes.xfrm & SGX_XFRM_MPX != 0) { @@ -129,6 +131,25 @@ pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 .unwrap_or(ecall_errno!(EFAULT)) } +#[no_mangle] +pub extern "C" fn occlum_ecall_kill(pid: i32, sig: i32) -> i32 { + if HAS_INIT.load(Ordering::SeqCst) == false { + return ecall_errno!(EAGAIN); + } + + let _ = unsafe { backtrace::enable_backtrace(&ENCLAVE_PATH, PrintFormat::Short) }; + panic::catch_unwind(|| { + backtrace::__rust_begin_short_backtrace(|| match do_kill(pid, sig) { + Ok(()) => 0, + Err(e) => { + eprintln!("failed to kill: {}", e.backtrace()); + ecall_errno!(e.errno()) + } + }) + }) + .unwrap_or(ecall_errno!(EFAULT)) +} + fn parse_log_level(level_chars: *const c_char) -> Result { const DEFAULT_LEVEL: LevelFilter = LevelFilter::Off; @@ -255,3 +276,23 @@ fn validate_program_path(target_path: &PathBuf) -> Result<()> { } Ok(()) } + +fn do_kill(pid: i32, sig: i32) -> Result<()> { + let filter = if pid > 0 { + ProcessFilter::WithPid(pid as pid_t) + } else if pid == -1 { + ProcessFilter::WithAnyPid + } else if pid < 0 { + return_errno!(EINVAL, "Invalid pid"); + } else { + // pid == 0 + return_errno!(EPERM, "Process 0 cannot be killed"); + }; + let signum = { + if sig < 0 { + return_errno!(EINVAL, "invalid arguments"); + } + SigNum::from_u8(sig as u8)? + }; + crate::signal::do_kill_from_outside_enclave(filter, signum) +} diff --git a/src/libos/src/signal/do_kill.rs b/src/libos/src/signal/do_kill.rs index 8cfe8d35..3b7b0c4e 100644 --- a/src/libos/src/signal/do_kill.rs +++ b/src/libos/src/signal/do_kill.rs @@ -1,4 +1,5 @@ -use super::signals::{UserSignal, UserSignalKind}; +use super::constants::*; +use super::signals::{KernelSignal, UserSignal, UserSignalKind}; use super::{SigNum, Signal}; use crate::prelude::*; use crate::process::{table, ProcessFilter, ProcessRef, ProcessStatus, ThreadRef, ThreadStatus}; @@ -21,6 +22,32 @@ pub fn do_kill(filter: ProcessFilter, signum: SigNum) -> Result<()> { Ok(()) } +/// Send a signal from the outside the enclave. +/// +/// Such a call must be performed very carefully. The obvious reason +/// is that the call is not trusted. And there is a less obvious reason: +/// the function is not executed during a normal syscall. Thus, current!() does +/// not refer to a valid LibOS thread. So let's implement this function with +/// these two insights in mind. +pub fn do_kill_from_outside_enclave(filter: ProcessFilter, signum: SigNum) -> Result<()> { + let signal = { + if signum != SIGKILL && signum != SIGTERM { + return_errno!(EPERM, "The signal is not allowed"); + } + Box::new(KernelSignal::new(signum)) + }; + let processes = get_processes(&filter)?; + for process in processes { + if process.status() == ProcessStatus::Zombie { + continue; + } + + let mut sig_queues = process.sig_queues().lock().unwrap(); + sig_queues.enqueue(signal.clone()); + } + Ok(()) +} + fn get_processes(filter: &ProcessFilter) -> Result> { let processes = match filter { ProcessFilter::WithAnyPid => table::get_all_processes(), diff --git a/src/libos/src/signal/mod.rs b/src/libos/src/signal/mod.rs index 2dcd29f9..f119fb61 100644 --- a/src/libos/src/signal/mod.rs +++ b/src/libos/src/signal/mod.rs @@ -6,6 +6,7 @@ use sig_action::{SigAction, SigActionFlags, SigDefaultAction}; pub use self::c_types::{sigaction_t, sigset_t}; pub use self::constants::*; +pub use self::do_kill::do_kill_from_outside_enclave; pub use self::do_sigreturn::{deliver_signal, force_signal}; pub use self::sig_dispositions::SigDispositions; pub use self::sig_num::SigNum; diff --git a/src/pal/include/occlum_pal_api.h b/src/pal/include/occlum_pal_api.h index 394e34f9..26679b1e 100644 --- a/src/pal/include/occlum_pal_api.h +++ b/src/pal/include/occlum_pal_api.h @@ -73,6 +73,18 @@ int occlum_pal_exec(const char* cmd_path, const struct occlum_stdio_fds* io_fds, int* exit_status); +/* + * @brief Send a signal to one or multiple LibOS processes + * + * @param pid If pid > 0, send the signal to the process with the + * pid; if pid == -1, send the signal to all processes. + * @param sig The signal number. For the purpose of security, the + * only allowed signals for now are SIGKILL and SIGTERM. + * + * @retval If 0, then success; otherwise, check errno for the exact error type. + */ +int occlum_pal_kill(int pid, int sig); + /* * @brief Destroy teh Occlum enclave * diff --git a/src/pal/src/pal_api.c b/src/pal/src/pal_api.c index cb28a189..1199f720 100644 --- a/src/pal/src/pal_api.c +++ b/src/pal/src/pal_api.c @@ -94,6 +94,32 @@ int occlum_pal_exec(const char* cmd_path, return 0; } +int occlum_pal_kill(int pid, int sig) { + errno = 0; + + sgx_enclave_id_t eid = pal_get_enclave_id(); + if (eid == SGX_INVALID_ENCLAVE_ID) { + errno = ENOENT; + PAL_ERROR("Enclave is not initialized yet."); + return -1; + } + + int ecall_ret = 0; + sgx_status_t ecall_status = occlum_ecall_kill(eid, &ecall_ret, pid, sig); + if (ecall_status != SGX_SUCCESS) { + const char* sgx_err = pal_get_sgx_error_msg(ecall_status); + PAL_ERROR("Failed to do ECall: %s", sgx_err); + return -1; + } + if (ecall_ret < 0) { + errno = -ecall_ret; + PAL_ERROR("Failed to occlum_ecall_kill: %s", errno2str(errno)); + return -1; + } + + return 0; +} + int occlum_pal_destroy(void) { errno = 0;