Add errno info for ECalls
Before this commit, the three ECalls of the LibOS enclave do not give the exact reason on error. In this commit, we modify the enclave entry code to return the errno and list all possible values of errno in Enclave.edl.
This commit is contained in:
parent
1c707eda30
commit
6e140a0d38
@ -13,7 +13,11 @@ enclave {
|
|||||||
/*
|
/*
|
||||||
* Initialize the LibOS according to the specified attributes.
|
* Initialize the LibOS according to the specified attributes.
|
||||||
*
|
*
|
||||||
* @retval On success, return 0; otherwise, return -1.
|
* @retval On success, return 0; otherwise, return -errno.
|
||||||
|
*
|
||||||
|
* The possible values of errno are
|
||||||
|
* EEXIST - The LibOS has already been initialized.
|
||||||
|
* EINVAL - The value of an argument are invalid.
|
||||||
*/
|
*/
|
||||||
public int occlum_ecall_init([in, string] const char* log_level, [in, string] const char* instance_dir);
|
public int occlum_ecall_init([in, string] const char* log_level, [in, string] const char* instance_dir);
|
||||||
|
|
||||||
@ -23,7 +27,13 @@ enclave {
|
|||||||
*
|
*
|
||||||
* @retval On success, return the thread ID of the
|
* @retval On success, return the thread ID of the
|
||||||
* newly-created process (pid == tid for a new process). On error,
|
* newly-created process (pid == tid for a new process). On error,
|
||||||
* return -1.
|
* return -errno.
|
||||||
|
*
|
||||||
|
* The possible values of errno are
|
||||||
|
* EAGAIN - The LibOS is not initialized.
|
||||||
|
* EINVAL - The value of an argument are invalid.
|
||||||
|
* ENOMEM - Not enough memory to create the new process.
|
||||||
|
* EACCES - The path of the executable is not accessible.
|
||||||
*/
|
*/
|
||||||
public int occlum_ecall_new_process(
|
public int occlum_ecall_new_process(
|
||||||
[in, string] const char* executable_path,
|
[in, string] const char* executable_path,
|
||||||
@ -33,10 +43,22 @@ enclave {
|
|||||||
/*
|
/*
|
||||||
* Execute the LibOS thread specified by the TID.
|
* Execute the LibOS thread specified by the TID.
|
||||||
*
|
*
|
||||||
* This API is synchronous: it returns until the LibOS thread exits.
|
* This API is synchronous: it returns until the LibOS thread
|
||||||
|
* terminates.
|
||||||
*
|
*
|
||||||
* @retval On success, return the exit status of the thread. On error,
|
* @retval On success, return the exit status of the thread, whose
|
||||||
* return -1.
|
* value is always greater than or equal to zero. To unify the two
|
||||||
|
* cases of terminating normally and being killed by a signal, the
|
||||||
|
* exit status is encoded in the way described in wait(2) man page.
|
||||||
|
* This means if the thread exits normally (e.g., returning from the
|
||||||
|
* main page or call exit(2) explictly), then the exit status is
|
||||||
|
* can be gained by WEXITSTATUS(status). If the thread is killed by a
|
||||||
|
* signal, then the signal number is WTERMSIG(status).
|
||||||
|
*
|
||||||
|
* The possible values of errno are
|
||||||
|
* EAGAIN - The LibOS is not initialized.
|
||||||
|
* EINVAL - The value of an argument are invalid.
|
||||||
|
* 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);
|
||||||
};
|
};
|
||||||
|
@ -19,10 +19,17 @@ lazy_static! {
|
|||||||
static ref HAS_INIT: AtomicBool = AtomicBool::new(false);
|
static ref HAS_INIT: AtomicBool = AtomicBool::new(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! ecall_errno {
|
||||||
|
($errno:expr) => {{
|
||||||
|
let errno: Errno = $errno;
|
||||||
|
-(errno as i32)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn occlum_ecall_init(log_level: *const c_char, instance_dir: *const c_char) -> i32 {
|
pub extern "C" fn occlum_ecall_init(log_level: *const c_char, instance_dir: *const c_char) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == true {
|
if HAS_INIT.load(Ordering::SeqCst) == true {
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return ecall_errno!(EEXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(!instance_dir.is_null());
|
assert!(!instance_dir.is_null());
|
||||||
@ -31,7 +38,7 @@ pub extern "C" fn occlum_ecall_init(log_level: *const c_char, instance_dir: *con
|
|||||||
let input_log_level = match parse_log_level(log_level) {
|
let input_log_level = match parse_log_level(log_level) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("invalid log level: {}", e.backtrace());
|
eprintln!("invalid log level: {}", e.backtrace());
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return ecall_errno!(EINVAL);
|
||||||
}
|
}
|
||||||
Ok(log_level) => log_level,
|
Ok(log_level) => log_level,
|
||||||
};
|
};
|
||||||
@ -75,14 +82,14 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
host_stdio_fds: *const HostStdioFds,
|
host_stdio_fds: *const HostStdioFds,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::SeqCst) == false {
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return ecall_errno!(EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (path, args, host_stdio_fds) = match parse_arguments(path_buf, argv, host_stdio_fds) {
|
let (path, args, host_stdio_fds) = match parse_arguments(path_buf, argv, host_stdio_fds) {
|
||||||
Ok(path_and_args_and_host_stdio_fds) => path_and_args_and_host_stdio_fds,
|
Ok(path_and_args_and_host_stdio_fds) => path_and_args_and_host_stdio_fds,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("invalid arguments for LibOS: {}", e.backtrace());
|
eprintln!("invalid arguments for LibOS: {}", e.backtrace());
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return ecall_errno!(e.errno());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -93,18 +100,18 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
Ok(pid_t) => pid_t as i32,
|
Ok(pid_t) => pid_t as i32,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("failed to boot up LibOS: {}", e.backtrace());
|
eprintln!("failed to boot up LibOS: {}", e.backtrace());
|
||||||
EXIT_STATUS_INTERNAL_ERROR
|
ecall_errno!(e.errno())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or(EXIT_STATUS_INTERNAL_ERROR)
|
.unwrap_or(ecall_errno!(EFAULT))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 {
|
pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 {
|
||||||
if HAS_INIT.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::SeqCst) == false {
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return ecall_errno!(EAGAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = unsafe { backtrace::enable_backtrace(&ENCLAVE_PATH, PrintFormat::Short) };
|
let _ = unsafe { backtrace::enable_backtrace(&ENCLAVE_PATH, PrintFormat::Short) };
|
||||||
@ -114,19 +121,14 @@ pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32
|
|||||||
Ok(exit_status) => exit_status,
|
Ok(exit_status) => exit_status,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("failed to execute a process: {}", e.backtrace());
|
eprintln!("failed to execute a process: {}", e.backtrace());
|
||||||
EXIT_STATUS_INTERNAL_ERROR
|
ecall_errno!(e.errno())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or(EXIT_STATUS_INTERNAL_ERROR)
|
.unwrap_or(ecall_errno!(EFAULT))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use -128 as a special value to indicate internal error from libos, not from
|
|
||||||
// user programs. The LibOS ensures that an user program can only return a
|
|
||||||
// value between 0 and 255 (inclusive).
|
|
||||||
const EXIT_STATUS_INTERNAL_ERROR: i32 = -128;
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -249,7 +251,7 @@ fn validate_program_path(target_path: &PathBuf) -> Result<()> {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|valid_path_prefix| target_path.starts_with(valid_path_prefix));
|
.any(|valid_path_prefix| target_path.starts_with(valid_path_prefix));
|
||||||
if !is_valid_entry_point {
|
if !is_valid_entry_point {
|
||||||
return_errno!(EINVAL, "program path is NOT a valid entry point");
|
return_errno!(EACCES, "program path is NOT a valid entry point");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ fn dequeue(libos_tid: pid_t) -> Result<ThreadRef> {
|
|||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.remove(&libos_tid)
|
.remove(&libos_tid)
|
||||||
.ok_or_else(|| errno!(EAGAIN, "the given TID does not match any pending thread"))
|
.ok_or_else(|| errno!(ESRCH, "the given TID does not match any pending thread"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the specified LibOS thread in the current host thread.
|
/// Execute the specified LibOS thread in the current host thread.
|
||||||
|
14
src/pal/include/errno2str.h
Normal file
14
src/pal/include/errno2str.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __ERRNO2STR_H__
|
||||||
|
#define __ERRNO2STR_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char* errno2str(int errno_);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __ERRNO2STR_H__ */
|
21
src/pal/src/errno2str.c
Normal file
21
src/pal/src/errno2str.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include "errno2str.h"
|
||||||
|
|
||||||
|
const char* errno2str(int errno_) {
|
||||||
|
switch(errno_) {
|
||||||
|
case EPERM: return "EPERM";
|
||||||
|
case ENOENT: return "ENOENT";
|
||||||
|
case ESRCH: return "ESRCH";
|
||||||
|
case ENOEXEC: return "ENOEXEC";
|
||||||
|
case EBADF: return "EBADF";
|
||||||
|
case ECHILD: return "ECHILD";
|
||||||
|
case EAGAIN: return "EAGAIN";
|
||||||
|
case ENOMEM: return "ENOMEM";
|
||||||
|
case EACCES: return "EACCES";
|
||||||
|
case EFAULT: return "EFAULT";
|
||||||
|
case EBUSY: return "EBUSY";
|
||||||
|
case EINVAL: return "EINVAL";
|
||||||
|
case ENOSYS: return "ENOSYS";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
#include "pal_error.h"
|
#include "pal_error.h"
|
||||||
#include "pal_log.h"
|
#include "pal_log.h"
|
||||||
#include "pal_syscall.h"
|
#include "pal_syscall.h"
|
||||||
|
#include "errno2str.h"
|
||||||
|
|
||||||
int occlum_pal_init(const struct occlum_pal_attr* attr) {
|
int occlum_pal_init(const struct occlum_pal_attr* attr) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
@ -27,21 +28,18 @@ int occlum_pal_init(const struct occlum_pal_attr* attr) {
|
|||||||
if (pal_init_enclave(attr->instance_dir) < 0) {
|
if (pal_init_enclave(attr->instance_dir) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke a do-nothing ECall for two purposes:
|
|
||||||
// 1) Test the enclave can work;
|
|
||||||
// 2) Initialize the global data structures inside the enclave (which is
|
|
||||||
// automatically done by Intel SGX SDK).
|
|
||||||
eid = pal_get_enclave_id();
|
eid = pal_get_enclave_id();
|
||||||
int ret;
|
|
||||||
sgx_status_t ecall_status = occlum_ecall_init(eid, &ret, attr->log_level, attr->instance_dir);
|
int ecall_ret = 0;
|
||||||
|
sgx_status_t ecall_status = occlum_ecall_init(eid, &ecall_ret, attr->log_level, attr->instance_dir);
|
||||||
if (ecall_status != SGX_SUCCESS) {
|
if (ecall_status != SGX_SUCCESS) {
|
||||||
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
|
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
|
||||||
PAL_ERROR("Failed to do ECall: %s", sgx_err);
|
PAL_ERROR("Failed to do ECall: %s", sgx_err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ecall_ret < 0) {
|
||||||
errno = EINVAL;
|
errno = -ecall_ret;
|
||||||
|
PAL_ERROR("occlum_ecall_init returns %s", errno2str(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -65,25 +63,34 @@ int occlum_pal_exec(const char* cmd_path,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int libos_tid = -1;
|
int ecall_ret = 0; // libos_tid
|
||||||
sgx_status_t ecall_status = occlum_ecall_new_process(eid, &libos_tid, cmd_path, cmd_args, io_fds);
|
sgx_status_t ecall_status = occlum_ecall_new_process(eid, &ecall_ret, cmd_path, cmd_args, io_fds);
|
||||||
if (ecall_status != SGX_SUCCESS) {
|
if (ecall_status != SGX_SUCCESS) {
|
||||||
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
|
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
|
||||||
PAL_ERROR("Failed to do ECall: %s", sgx_err);
|
PAL_ERROR("Failed to do ECall: %s", sgx_err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (libos_tid < 0) {
|
if (ecall_ret < 0) {
|
||||||
|
errno = -ecall_ret;
|
||||||
|
PAL_ERROR("occlum_ecall_new_process returns %s", errno2str(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int libos_tid = ecall_ret;
|
||||||
int host_tid = gettid();
|
int host_tid = gettid();
|
||||||
ecall_status = occlum_ecall_exec_thread(eid, exit_status, libos_tid, host_tid);
|
ecall_status = occlum_ecall_exec_thread(eid, &ecall_ret, libos_tid, host_tid);
|
||||||
if (ecall_status != SGX_SUCCESS) {
|
if (ecall_status != SGX_SUCCESS) {
|
||||||
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
|
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
|
||||||
PAL_ERROR("Failed to do ECall: %s", sgx_err);
|
PAL_ERROR("Failed to do ECall: %s", sgx_err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (ecall_ret < 0) {
|
||||||
|
errno = -ecall_ret;
|
||||||
|
PAL_ERROR("occlum_ecall_exec_thread returns %s", errno2str(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*exit_status = ecall_ret;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user