Add the support for setting log level at runtime
Now one can specify the log level of the LibOS by setting `OCCLUM_LOG_LEVEL` environment variable. The possible values are "off", "error", "warn", "info", and "trace". However, for the sake of security, the log level of a release enclave (DisableDebug = 1 in Enclave.xml) is always "off" (i.e., no log) regardless of the log level specified by the untrusted environment.
This commit is contained in:
parent
6d7cf7b9f6
commit
9713e74ed9
@ -10,13 +10,20 @@ enclave {
|
|||||||
include "occlum_edl_types.h"
|
include "occlum_edl_types.h"
|
||||||
|
|
||||||
trusted {
|
trusted {
|
||||||
|
/*
|
||||||
|
* Initialize the LibOS according to the specified attributes.
|
||||||
|
*
|
||||||
|
* @retval On success, return 0; otherwise, return -1.
|
||||||
|
*/
|
||||||
|
public int occlum_ecall_init([in, string] const char* log_level);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a new LibOS process to do the task specified by the given
|
* Create a new LibOS process to do the task specified by the given
|
||||||
* arguments.
|
* arguments.
|
||||||
*
|
*
|
||||||
* @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. The user can check errno for the concrete error type.
|
* return -1.
|
||||||
*/
|
*/
|
||||||
public int occlum_ecall_new_process(
|
public int occlum_ecall_new_process(
|
||||||
[in, string] const char* executable_path,
|
[in, string] const char* executable_path,
|
||||||
@ -28,14 +35,9 @@ enclave {
|
|||||||
* This API is synchronous: it returns until the LibOS thread exits.
|
* This API is synchronous: it returns until the LibOS thread exits.
|
||||||
*
|
*
|
||||||
* @retval On success, return the exit status of the thread. On error,
|
* @retval On success, return the exit status of the thread. On error,
|
||||||
* return -1. The user can check errno for the concrete error type.
|
* return -1.
|
||||||
*/
|
*/
|
||||||
public int occlum_ecall_exec_thread(int libos_tid, int host_tid);
|
public int occlum_ecall_exec_thread(int libos_tid, int host_tid);
|
||||||
|
|
||||||
/*
|
|
||||||
* A do-nothing ECall for debug purpose only.
|
|
||||||
*/
|
|
||||||
public void occlum_ecall_nop(void);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
untrusted {
|
untrusted {
|
||||||
|
@ -5,13 +5,51 @@ 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 util::mem_util::from_untrusted::*;
|
||||||
|
use util::sgx::allow_debug as sgx_allow_debug;
|
||||||
|
|
||||||
const ENCLAVE_PATH: &'static str = ".occlum/build/lib/libocclum-libos.signed.so";
|
const ENCLAVE_PATH: &'static str = ".occlum/build/lib/libocclum-libos.signed.so";
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref INIT_ONCE: Once = Once::new();
|
static ref INIT_ONCE: Once = Once::new();
|
||||||
static ref ALLOW_RUN: AtomicBool = AtomicBool::new(false);
|
static ref HAS_INIT: AtomicBool = AtomicBool::new(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn occlum_ecall_init(log_level: *const c_char) -> i32 {
|
||||||
|
if HAS_INIT.load(Ordering::SeqCst) == true {
|
||||||
|
return EXIT_STATUS_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
let log_level = {
|
||||||
|
let input_log_level = match parse_log_level(log_level) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("invalid log level: {}", e.backtrace());
|
||||||
|
return EXIT_STATUS_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
Ok(log_level) => log_level,
|
||||||
|
};
|
||||||
|
// Use the input log level if and only if the enclave allows debug
|
||||||
|
if sgx_allow_debug() {
|
||||||
|
input_log_level
|
||||||
|
} else {
|
||||||
|
LevelFilter::Off
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
util::mpx_util::mpx_enable();
|
||||||
|
// Register exception handlers (support cpuid & rdtsc for now)
|
||||||
|
register_exception_handlers();
|
||||||
|
|
||||||
|
HAS_INIT.store(true, Ordering::SeqCst);
|
||||||
|
});
|
||||||
|
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -19,25 +57,9 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
path_buf: *const c_char,
|
path_buf: *const c_char,
|
||||||
argv: *const *const c_char,
|
argv: *const *const c_char,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
INIT_ONCE.call_once(|| {
|
if HAS_INIT.load(Ordering::SeqCst) == false {
|
||||||
// Init the log infrastructure first so that log messages will be printed afterwards
|
return EXIT_STATUS_INTERNAL_ERROR;
|
||||||
use util::log::LevelFilter;
|
}
|
||||||
let log_level = match option_env!("LIBOS_LOG") {
|
|
||||||
Some("error") => LevelFilter::Error,
|
|
||||||
Some("warn") => LevelFilter::Warn,
|
|
||||||
Some("info") => LevelFilter::Info,
|
|
||||||
Some("debug") => LevelFilter::Debug,
|
|
||||||
Some("trace") => LevelFilter::Trace,
|
|
||||||
_ => LevelFilter::Error, // errors are printed be default
|
|
||||||
};
|
|
||||||
util::log::init(log_level);
|
|
||||||
// Init MPX for SFI
|
|
||||||
util::mpx_util::mpx_enable();
|
|
||||||
// Register exception handlers (support cpuid & rdtsc for now)
|
|
||||||
register_exception_handlers();
|
|
||||||
|
|
||||||
ALLOW_RUN.store(true, Ordering::SeqCst);
|
|
||||||
});
|
|
||||||
|
|
||||||
let (path, args) = match parse_arguments(path_buf, argv) {
|
let (path, args) = match parse_arguments(path_buf, argv) {
|
||||||
Ok(path_and_args) => path_and_args,
|
Ok(path_and_args) => path_and_args,
|
||||||
@ -61,7 +83,7 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
|
|
||||||
#[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 ALLOW_RUN.load(Ordering::SeqCst) == false {
|
if HAS_INIT.load(Ordering::SeqCst) == false {
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return EXIT_STATUS_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,14 +102,36 @@ pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32
|
|||||||
.unwrap_or(EXIT_STATUS_INTERNAL_ERROR)
|
.unwrap_or(EXIT_STATUS_INTERNAL_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn occlum_ecall_nop() {}
|
|
||||||
|
|
||||||
// Use -128 as a special value to indicate internal error from libos, not from
|
// 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
|
// user programs. The LibOS ensures that an user program can only return a
|
||||||
// value between 0 and 255 (inclusive).
|
// value between 0 and 255 (inclusive).
|
||||||
const EXIT_STATUS_INTERNAL_ERROR: i32 = -128;
|
const EXIT_STATUS_INTERNAL_ERROR: i32 = -128;
|
||||||
|
|
||||||
|
fn parse_log_level(level_chars: *const c_char) -> Result<LevelFilter> {
|
||||||
|
const DEFAULT_LEVEL: LevelFilter = LevelFilter::Off;
|
||||||
|
|
||||||
|
if level_chars.is_null() {
|
||||||
|
return Ok(DEFAULT_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
let level_string = {
|
||||||
|
let level_cstring = clone_cstring_safely(level_chars)?;
|
||||||
|
level_cstring
|
||||||
|
.into_string()
|
||||||
|
.map_err(|e| errno!(EINVAL, "log_level contains valid utf-8 data"))?
|
||||||
|
.to_lowercase()
|
||||||
|
};
|
||||||
|
Ok(match level_string.as_str() {
|
||||||
|
"off" => LevelFilter::Off,
|
||||||
|
"error" => LevelFilter::Error,
|
||||||
|
"warn" => LevelFilter::Warn,
|
||||||
|
"info" => LevelFilter::Info,
|
||||||
|
"debug" => LevelFilter::Debug,
|
||||||
|
"trace" => LevelFilter::Trace,
|
||||||
|
_ => DEFAULT_LEVEL, // Default
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_arguments(
|
fn parse_arguments(
|
||||||
path_ptr: *const c_char,
|
path_ptr: *const c_char,
|
||||||
argv: *const *const c_char,
|
argv: *const *const c_char,
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod attestation;
|
|
||||||
mod consts;
|
mod consts;
|
||||||
|
|
||||||
use self::attestation::*;
|
|
||||||
use self::consts::*;
|
use self::consts::*;
|
||||||
|
use util::sgx::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DevSgx;
|
pub struct DevSgx;
|
||||||
|
@ -96,7 +96,9 @@ impl Log for SimpleLogger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn flush(&self) {
|
fn flush(&self) {
|
||||||
//unsafe { occlum_ocall_flush_log(); }
|
unsafe {
|
||||||
|
occlum_ocall_flush_log();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +93,9 @@ pub mod from_untrusted {
|
|||||||
// TODO: strict check!
|
// TODO: strict check!
|
||||||
pub fn clone_cstring_safely(out_ptr: *const c_char) -> Result<CString> {
|
pub fn clone_cstring_safely(out_ptr: *const c_char) -> Result<CString> {
|
||||||
check_ptr(out_ptr)?;
|
check_ptr(out_ptr)?;
|
||||||
|
if out_ptr.is_null() {
|
||||||
|
return_errno!(EINVAL, "null ptr");
|
||||||
|
}
|
||||||
// TODO: using from_ptr directly is not safe
|
// TODO: using from_ptr directly is not safe
|
||||||
let cstr = unsafe { CStr::from_ptr(out_ptr) };
|
let cstr = unsafe { CStr::from_ptr(out_ptr) };
|
||||||
let cstring = CString::from(cstr);
|
let cstring = CString::from(cstr);
|
||||||
|
@ -4,3 +4,4 @@ pub mod log;
|
|||||||
pub mod mem_util;
|
pub mod mem_util;
|
||||||
pub mod mpx_util;
|
pub mod mpx_util;
|
||||||
pub mod ring_buf;
|
pub mod ring_buf;
|
||||||
|
pub mod sgx;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! SGX attestation.
|
//! SGX utility.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -17,3 +17,8 @@ pub use sgx_types::{
|
|||||||
pub use self::sgx_attestation_agent::SgxAttestationAgent;
|
pub use self::sgx_attestation_agent::SgxAttestationAgent;
|
||||||
pub use self::sgx_quote::SgxQuote;
|
pub use self::sgx_quote::SgxQuote;
|
||||||
pub use self::sgx_report::{create_report, get_self_target, verify_report};
|
pub use self::sgx_report::{create_report, get_self_target, verify_report};
|
||||||
|
|
||||||
|
pub fn allow_debug() -> bool {
|
||||||
|
let self_report = create_report(None, None).expect("create a self report should never fail");
|
||||||
|
(self_report.body.attributes.flags & SGX_FLAGS_DEBUG) == SGX_FLAGS_DEBUG
|
||||||
|
}
|
@ -5,20 +5,42 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Occlum PAL attributes
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// Occlum instance dir.
|
||||||
|
//
|
||||||
|
// Specifies the path of an Occlum instance directory. Usually, this
|
||||||
|
// directory is initialized by executing "occlum init" command, which
|
||||||
|
// creates a hidden directory named ".occlum/". This ".occlum/" is an
|
||||||
|
// Occlum instance directory. The name of the directory is not necesarrily
|
||||||
|
// ".occlum"; it can be renamed to an arbitrary name.
|
||||||
|
//
|
||||||
|
// Mandatory field. Must not be NULL.
|
||||||
|
const char* instance_dir;
|
||||||
|
// Log level.
|
||||||
|
//
|
||||||
|
// Specifies the log level of Occlum LibOS. Valid values: "off", "error",
|
||||||
|
// "warn", "info", and "trace". Case insensitive.
|
||||||
|
//
|
||||||
|
// Optional field. If NULL, the LibOS will treat it as "off".
|
||||||
|
const char* log_level;
|
||||||
|
} occlum_pal_attr_t;
|
||||||
|
|
||||||
|
#define OCCLUM_PAL_ATTR_INITVAL { \
|
||||||
|
.instance_dir = NULL, \
|
||||||
|
.log_level = NULL \
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Initialize an Occlum enclave
|
* @brief Initialize an Occlum enclave
|
||||||
*
|
*
|
||||||
* @param instance_dir Specifies the path of an Occlum instance directory.
|
* @param attr Mandatory input. Attributes for Occlum.
|
||||||
* Usually, this directory is initialized by executing
|
|
||||||
* "occlum init" command, which creates a hidden
|
|
||||||
* directory named ".occlum/". This ".occlum/" is an
|
|
||||||
* Occlum instance directory. The name of the directory is
|
|
||||||
* not necesarrily ".occlum"; it can be renamed to an
|
|
||||||
* arbitrary name.
|
|
||||||
*
|
*
|
||||||
* @retval If 0, then success; otherwise, check errno for the exact error type.
|
* @retval If 0, then success; otherwise, check errno for the exact error type.
|
||||||
*/
|
*/
|
||||||
int occlum_pal_init(const char* instance_dir);
|
int occlum_pal_init(occlum_pal_attr_t* attr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Execute a command inside the Occlum enclave
|
* @brief Execute a command inside the Occlum enclave
|
||||||
|
@ -5,10 +5,14 @@
|
|||||||
#include "pal_log.h"
|
#include "pal_log.h"
|
||||||
#include "pal_syscall.h"
|
#include "pal_syscall.h"
|
||||||
|
|
||||||
int occlum_pal_init(const char* instance_dir) {
|
int occlum_pal_init(occlum_pal_attr_t* attr) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
if (instance_dir == NULL) {
|
if (attr == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (attr->instance_dir == NULL) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -20,7 +24,7 @@ int occlum_pal_init(const char* instance_dir) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pal_init_enclave(instance_dir) < 0) {
|
if (pal_init_enclave(attr->instance_dir) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,13 +33,17 @@ int occlum_pal_init(const char* instance_dir) {
|
|||||||
// 2) Initialize the global data structures inside the enclave (which is
|
// 2) Initialize the global data structures inside the enclave (which is
|
||||||
// automatically done by Intel SGX SDK).
|
// automatically done by Intel SGX SDK).
|
||||||
eid = pal_get_enclave_id();
|
eid = pal_get_enclave_id();
|
||||||
sgx_status_t ecall_status = occlum_ecall_nop(eid);
|
int ret;
|
||||||
|
sgx_status_t ecall_status = occlum_ecall_init(eid, &ret, attr->log_level);
|
||||||
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) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,10 @@ int main(int argc, char* argv[]) {
|
|||||||
const char** cmd_args = (const char**) &argv[2];
|
const char** cmd_args = (const char**) &argv[2];
|
||||||
|
|
||||||
// Init Occlum PAL
|
// Init Occlum PAL
|
||||||
const char* instance_dir = get_instance_dir();
|
occlum_pal_attr_t attr = OCCLUM_PAL_ATTR_INITVAL;
|
||||||
if (occlum_pal_init(instance_dir) < 0) {
|
attr.instance_dir = get_instance_dir();
|
||||||
|
attr.log_level = getenv("OCCLUM_LOG_LEVEL");
|
||||||
|
if (occlum_pal_init(&attr) < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
// Helper macros
|
// Helper macros
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#define NTHREADS (4)
|
#define NTHREADS (3)
|
||||||
#define STACK_SIZE (8 * 1024)
|
#define STACK_SIZE (8 * 1024)
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// The test case of concurrent counter
|
// The test case of concurrent counter
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#define LOCAL_COUNT (100000UL)
|
#define LOCAL_COUNT (1000UL)
|
||||||
#define EXPECTED_GLOBAL_COUNT (LOCAL_COUNT * NTHREADS)
|
#define EXPECTED_GLOBAL_COUNT (LOCAL_COUNT * NTHREADS)
|
||||||
|
|
||||||
struct thread_arg {
|
struct thread_arg {
|
||||||
|
Loading…
Reference in New Issue
Block a user