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:
Tate, Hongliang Tian 2020-03-24 14:59:36 +00:00
parent 6d7cf7b9f6
commit 9713e74ed9
15 changed files with 143 additions and 55 deletions

@ -10,13 +10,20 @@ enclave {
include "occlum_edl_types.h"
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
* arguments.
*
* @retval On success, return the thread ID of the
* 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(
[in, string] const char* executable_path,
@ -28,14 +35,9 @@ enclave {
* This API is synchronous: it returns until the LibOS thread exits.
*
* @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);
/*
* A do-nothing ECall for debug purpose only.
*/
public void occlum_ecall_nop(void);
};
untrusted {

@ -5,13 +5,51 @@ 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;
const ENCLAVE_PATH: &'static str = ".occlum/build/lib/libocclum-libos.signed.so";
lazy_static! {
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]
@ -19,25 +57,9 @@ pub extern "C" fn occlum_ecall_new_process(
path_buf: *const c_char,
argv: *const *const c_char,
) -> i32 {
INIT_ONCE.call_once(|| {
// Init the log infrastructure first so that log messages will be printed afterwards
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);
});
if HAS_INIT.load(Ordering::SeqCst) == false {
return EXIT_STATUS_INTERNAL_ERROR;
}
let (path, args) = match parse_arguments(path_buf, argv) {
Ok(path_and_args) => path_and_args,
@ -61,7 +83,7 @@ pub extern "C" fn occlum_ecall_new_process(
#[no_mangle]
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;
}
@ -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)
}
#[no_mangle]
pub extern "C" fn occlum_ecall_nop() {}
// 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> {
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(
path_ptr: *const c_char,
argv: *const *const c_char,

@ -2,11 +2,10 @@
use super::*;
mod attestation;
mod consts;
use self::attestation::*;
use self::consts::*;
use util::sgx::*;
#[derive(Debug)]
pub struct DevSgx;

@ -96,7 +96,9 @@ impl Log for SimpleLogger {
}
}
fn flush(&self) {
//unsafe { occlum_ocall_flush_log(); }
unsafe {
occlum_ocall_flush_log();
}
}
}

@ -93,6 +93,9 @@ pub mod from_untrusted {
// TODO: strict check!
pub fn clone_cstring_safely(out_ptr: *const c_char) -> Result<CString> {
check_ptr(out_ptr)?;
if out_ptr.is_null() {
return_errno!(EINVAL, "null ptr");
}
// TODO: using from_ptr directly is not safe
let cstr = unsafe { CStr::from_ptr(out_ptr) };
let cstring = CString::from(cstr);

@ -4,3 +4,4 @@ pub mod log;
pub mod mem_util;
pub mod mpx_util;
pub mod ring_buf;
pub mod sgx;

@ -1,4 +1,4 @@
//! SGX attestation.
//! SGX utility.
use super::*;
@ -17,3 +17,8 @@ pub use sgx_types::{
pub use self::sgx_attestation_agent::SgxAttestationAgent;
pub use self::sgx_quote::SgxQuote;
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" {
#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
*
* @param 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.
* @param attr Mandatory input. Attributes for Occlum.
*
* @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

@ -5,10 +5,14 @@
#include "pal_log.h"
#include "pal_syscall.h"
int occlum_pal_init(const char* instance_dir) {
int occlum_pal_init(occlum_pal_attr_t* attr) {
errno = 0;
if (instance_dir == NULL) {
if (attr == NULL) {
errno = EINVAL;
return -1;
}
if (attr->instance_dir == NULL) {
errno = EINVAL;
return -1;
}
@ -20,7 +24,7 @@ int occlum_pal_init(const char* instance_dir) {
return -1;
}
if (pal_init_enclave(instance_dir) < 0) {
if (pal_init_enclave(attr->instance_dir) < 0) {
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
// automatically done by Intel SGX SDK).
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) {
const char* sgx_err = pal_get_sgx_error_msg(ecall_status);
PAL_ERROR("Failed to do ECall: %s", sgx_err);
return -1;
}
if (ret < 0) {
errno = EINVAL;
return -1;
}
return 0;
}

@ -24,8 +24,10 @@ int main(int argc, char* argv[]) {
const char** cmd_args = (const char**) &argv[2];
// Init Occlum PAL
const char* instance_dir = get_instance_dir();
if (occlum_pal_init(instance_dir) < 0) {
occlum_pal_attr_t attr = OCCLUM_PAL_ATTR_INITVAL;
attr.instance_dir = get_instance_dir();
attr.log_level = getenv("OCCLUM_LOG_LEVEL");
if (occlum_pal_init(&attr) < 0) {
return EXIT_FAILURE;
}

@ -8,14 +8,14 @@
// Helper macros
// ============================================================================
#define NTHREADS (4)
#define NTHREADS (3)
#define STACK_SIZE (8 * 1024)
// ============================================================================
// The test case of concurrent counter
// ============================================================================
#define LOCAL_COUNT (100000UL)
#define LOCAL_COUNT (1000UL)
#define EXPECTED_GLOBAL_COUNT (LOCAL_COUNT * NTHREADS)
struct thread_arg {