Add new API occlum_pal_kill

This API enables sending signals to one or multiple LibOS processes from
outside the enclave.
This commit is contained in:
Tate, Hongliang Tian 2020-05-01 14:56:18 +00:00
parent 6e140a0d38
commit 1d1330772c
6 changed files with 133 additions and 9 deletions

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

@ -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<LevelFilter> {
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)
}

@ -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<Vec<ProcessRef>> {
let processes = match filter {
ProcessFilter::WithAnyPid => table::get_all_processes(),

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

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

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