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:
parent
6e140a0d38
commit
1d1330772c
@ -61,6 +61,23 @@ enclave {
|
|||||||
* ESRCH - Cannot find the LibOS thread.
|
* ESRCH - Cannot find the LibOS thread.
|
||||||
*/
|
*/
|
||||||
public int occlum_ecall_exec_thread(int libos_tid, int host_tid);
|
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 {
|
untrusted {
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use super::*;
|
|
||||||
use exception::*;
|
|
||||||
use fs::HostStdioFds;
|
|
||||||
use process::pid_t;
|
|
||||||
use std::ffi::{CStr, CString, OsString};
|
use std::ffi::{CStr, CString, OsString};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
use util::log::LevelFilter;
|
|
||||||
use util::mem_util::from_untrusted::*;
|
use super::*;
|
||||||
use util::sgx::allow_debug as sgx_allow_debug;
|
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::*;
|
use sgx_tse::*;
|
||||||
|
|
||||||
pub static mut INSTANCE_DIR: String = String::new();
|
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_ONCE.call_once(|| {
|
||||||
// Init the log infrastructure first so that log messages will be printed afterwards
|
// Init the log infrastructure first so that log messages will be printed afterwards
|
||||||
util::log::init(log_level);
|
util::log::init(log_level);
|
||||||
|
|
||||||
// Init MPX for SFI if MPX is available
|
// Init MPX for SFI if MPX is available
|
||||||
let report = rsgx_self_report();
|
let report = rsgx_self_report();
|
||||||
if (report.body.attributes.xfrm & SGX_XFRM_MPX != 0) {
|
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))
|
.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> {
|
fn parse_log_level(level_chars: *const c_char) -> Result<LevelFilter> {
|
||||||
const DEFAULT_LEVEL: LevelFilter = LevelFilter::Off;
|
const DEFAULT_LEVEL: LevelFilter = LevelFilter::Off;
|
||||||
|
|
||||||
@ -255,3 +276,23 @@ fn validate_program_path(target_path: &PathBuf) -> Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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 super::{SigNum, Signal};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::process::{table, ProcessFilter, ProcessRef, ProcessStatus, ThreadRef, ThreadStatus};
|
use crate::process::{table, ProcessFilter, ProcessRef, ProcessStatus, ThreadRef, ThreadStatus};
|
||||||
@ -21,6 +22,32 @@ pub fn do_kill(filter: ProcessFilter, signum: SigNum) -> Result<()> {
|
|||||||
Ok(())
|
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>> {
|
fn get_processes(filter: &ProcessFilter) -> Result<Vec<ProcessRef>> {
|
||||||
let processes = match filter {
|
let processes = match filter {
|
||||||
ProcessFilter::WithAnyPid => table::get_all_processes(),
|
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::c_types::{sigaction_t, sigset_t};
|
||||||
pub use self::constants::*;
|
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::do_sigreturn::{deliver_signal, force_signal};
|
||||||
pub use self::sig_dispositions::SigDispositions;
|
pub use self::sig_dispositions::SigDispositions;
|
||||||
pub use self::sig_num::SigNum;
|
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,
|
const struct occlum_stdio_fds* io_fds,
|
||||||
int* exit_status);
|
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
|
* @brief Destroy teh Occlum enclave
|
||||||
*
|
*
|
||||||
|
@ -94,6 +94,32 @@ int occlum_pal_exec(const char* cmd_path,
|
|||||||
return 0;
|
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) {
|
int occlum_pal_destroy(void) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user