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 | ||||||
|  | } | ||||||
| @ -1,8 +1,8 @@ | |||||||
| #ifndef __OCCLUM_EDL_TYPES__ | #ifndef __OCCLUM_EDL_TYPES__ | ||||||
| #define __OCCLUM_EDL_TYPES__ | #define __OCCLUM_EDL_TYPES__ | ||||||
| 
 | 
 | ||||||
| #include <time.h>       // import struct timespec | #include <time.h>               // import struct timespec | ||||||
| #include <sys/time.h>   // import struct timeval | #include <sys/time.h>           // import struct timeval | ||||||
| #include <sys/uio.h>    // import struct iovec | #include <sys/uio.h>            // import struct iovec | ||||||
| 
 | 
 | ||||||
| #endif /* __OCCLUM_EDL_TYPES__ */ | #endif /* __OCCLUM_EDL_TYPES__ */ | ||||||
|  | |||||||
| @ -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