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" | ||||
| 
 | ||||
|     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 | ||||
| } | ||||
| @ -1,8 +1,8 @@ | ||||
| #ifndef __OCCLUM_EDL_TYPES__ | ||||
| #define __OCCLUM_EDL_TYPES__ | ||||
| 
 | ||||
| #include <time.h>       // import struct timespec | ||||
| #include <sys/time.h>   // import struct timeval | ||||
| #include <sys/uio.h>    // import struct iovec | ||||
| #include <time.h>               // import struct timespec | ||||
| #include <sys/time.h>           // import struct timeval | ||||
| #include <sys/uio.h>            // import struct iovec | ||||
| 
 | ||||
| #endif /* __OCCLUM_EDL_TYPES__ */ | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user