diff --git a/demos/runtime_boot/README.md b/demos/runtime_boot/README.md index f4722fd7..47639e53 100644 --- a/demos/runtime_boot/README.md +++ b/demos/runtime_boot/README.md @@ -25,6 +25,8 @@ The second parameter `rootfs_config` needs to be set as NULL. But for runtime booting pre-generated UnionFS image, The first parameter `key_ptr` is must to have, the second parameter `rootfs_config` needs have valid members. ``` struct user_rootfs_config { + // length of the struct + len: usize, // UnionFS type rootfs upper layer, read-write layer upper_layer_path: *const i8, // UnionFS type rootfs lower layer, read-only layer @@ -34,11 +36,18 @@ struct user_rootfs_config { hostfs_source: *const i8, // HostFS target path, default value is "/host" hostfs_target: *const i8, + // An array of pointers to null-terminated strings + // and must be terminated by a null pointer + envp: *const *const i8, } ``` In this demo, parameters values are provided as below. +* **len** +The length of the struct which should be the value of `size_of(user_rootfs_config)`. +It is helpful for possible future extension. + * **rootfs_key** The key to encrypt/decrypt the rootfs, here it is `"c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61". @@ -54,6 +63,10 @@ The entry point of the rootfs. In his case, it is `"/bin"`. * **hostfs_source** It is set to be `/tmp` in this case. +* **envp** +An array of pointers to null-terminated strings and must be terminated by a null pointer. +For example, set it to the address of ["TEST=1234", "TEST2=4567", NULL]. + In this example customized init, the above parameters are declared in the source [`main.rs`](./init/src/main.rs). In real case, they could be acquired by LA/RA or by modifying the PAL api `pal_run_init_process`. ### Build a boot template Occlum instance @@ -71,5 +84,7 @@ After running the script, runtime boot BASH could be done as below even if the d # occlum run /bin/occlum_bash_test.sh ``` - - +Also, the runtime environment passed by **envp** could be verified by +``` +# occlum run /bin/busybox env +``` diff --git a/demos/runtime_boot/init/src/main.rs b/demos/runtime_boot/init/src/main.rs index b68cc580..f97ed1e8 100644 --- a/demos/runtime_boot/init/src/main.rs +++ b/demos/runtime_boot/init/src/main.rs @@ -9,15 +9,11 @@ use std::error::Error; use std::fs::File; use std::io::{ErrorKind, Read}; +use std::mem::size_of; use std::ffi::CString; -use std::env; - fn main() -> Result<(), Box> { - let args: Vec = env::args().collect(); - println!("{:?}", args); - // TODO: Get the rootfs key and other parameters through RA/LA or PAL let rootfs_key = b"c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61"; let rootfs_upper_layer = "../gen_rootfs_instance/mnt_unionfs/upper"; @@ -53,12 +49,21 @@ fn main() -> Result<(), Box> { CString::new(rootfs_lower_layer).expect("CString::new failed"); let entry_point = CString::new(rootfs_entry).expect("CString::new failed"); let hostfs_source = CString::new("/tmp").expect("CString::new failed"); + + // Example envs. must end with null + let env1 = CString::new("TEST=1234").unwrap(); + let env2 = CString::new("OCCLUM=NO").unwrap(); + let env3 = CString::new("TEST2=5678").unwrap(); + let envp = [env1.as_ptr(), env2.as_ptr(), env3.as_ptr(), std::ptr::null()]; + let rootfs_config: user_rootfs_config = user_rootfs_config { + len: size_of::(), upper_layer_path: upper_layer_path.as_ptr(), lower_layer_path: lower_layer_path.as_ptr(), entry_point: entry_point.as_ptr(), hostfs_source: hostfs_source.as_ptr(), - hostfs_target: std::ptr::null() + hostfs_target: std::ptr::null(), + envp: envp.as_ptr() }; let ret = unsafe { syscall( @@ -80,6 +85,8 @@ type sgx_key_128bit_t = [u8; 16]; #[derive(Debug, Copy, Clone)] #[allow(non_camel_case_types)] struct user_rootfs_config { + // length of the struct + len: usize, // UnionFS type rootfs upper layer, read-write layer upper_layer_path: *const i8, // UnionFS type rootfs lower layer, read-only layer @@ -89,6 +96,9 @@ struct user_rootfs_config { hostfs_source: *const i8, // HostFS target path, default value is "/host" hostfs_target: *const i8, + // An array of pointers to null-terminated strings + // and must be terminated by a null pointer + envp: *const *const i8, } fn load_key(key_path: &str) -> Result> { diff --git a/src/libos/src/config.rs b/src/libos/src/config.rs index 68acb1c6..139a3715 100644 --- a/src/libos/src/config.rs +++ b/src/libos/src/config.rs @@ -22,6 +22,12 @@ lazy_static! { }; } +// Envs merged from default envs and possible envs passed by syscall do_mount_rootfs +lazy_static! { + pub static ref TRUSTED_ENVS: RwLock> = + RwLock::new(LIBOS_CONFIG.env.default.clone()); +} + pub fn load_config(config_path: &str, expected_mac: &sgx_aes_gcm_128bit_tag_t) -> Result { let mut config_file = { let config_file = SgxFile::open_integrity_only(config_path).map_err(|e| errno!(e))?; diff --git a/src/libos/src/entry.rs b/src/libos/src/entry.rs index e3c341f5..928cd091 100644 --- a/src/libos/src/entry.rs +++ b/src/libos/src/entry.rs @@ -390,8 +390,10 @@ fn merge_env(env: *const *const c_char) -> Result> { helper: HashMap::new(), }; + let config_env_trusted = crate::config::TRUSTED_ENVS.read().unwrap(); + // Use inner struct to parse env default - for (idx, val) in config::LIBOS_CONFIG.env.default.iter().enumerate() { + for (idx, val) in config_env_trusted.iter().enumerate() { env_default.content.push(CString::new(val.clone())?); let kv: Vec<&str> = val.to_str().unwrap().splitn(2, '=').collect(); // only split the first "=" env_default.helper.insert(kv[0].to_string(), idx); diff --git a/src/libos/src/fs/rootfs.rs b/src/libos/src/fs/rootfs.rs index ca2eafdf..b3a5c42c 100644 --- a/src/libos/src/fs/rootfs.rs +++ b/src/libos/src/fs/rootfs.rs @@ -4,6 +4,7 @@ use super::procfs::ProcFS; use super::sefs::{SgxStorage, SgxUuidProvider}; use super::*; use config::{ConfigApp, ConfigMountFsType}; +use std::mem::size_of; use std::path::{Path, PathBuf}; use std::untrusted::path::PathEx; @@ -270,11 +271,20 @@ fn open_or_create_sefs_according_to( #[derive(Debug, Copy, Clone)] #[allow(non_camel_case_types)] pub struct user_rootfs_config { + // length of the struct + len: usize, + // UnionFS type rootfs upper layer, read-write layer upper_layer_path: *const i8, + // UnionFS type rootfs lower layer, read-only layer lower_layer_path: *const i8, entry_point: *const i8, + // HostFS source path hostfs_source: *const i8, + // HostFS target path, default value is "/host" hostfs_target: *const i8, + // An array of pointers to null-terminated strings + // and must be terminated by a null pointer + envp: *const *const i8, } impl user_rootfs_config { @@ -298,7 +308,45 @@ fn to_option_pathbuf(path: *const i8) -> Result> { Ok(path) } +fn combine_trusted_envs(envp: *const *const i8) -> Result<()> { + let mut user_envs = from_user::clone_cstrings_safely(envp)?; + trace!("User envs: {:?}", user_envs); + let env_key: Vec<&str> = user_envs + .iter() + .map(|x| { + let kv: Vec<&str> = x.to_str().unwrap().splitn(2, '=').collect(); + kv[0] + }) + .collect(); + + let mut merged = config::TRUSTED_ENVS.write().unwrap(); + // First clear the default envs then do the merge again + merged.clear(); + merged.extend_from_slice(&user_envs); + + for (_idx, val) in config::LIBOS_CONFIG.env.default.iter().enumerate() { + let kv: Vec<&str> = val.to_str().unwrap().splitn(2, '=').collect(); // only split the first "=" + info!("kv: {:?}", kv); + if !env_key.contains(&kv[0]) { + unsafe { merged.push(val.clone()) }; + } + } + + // trace!("Combined trusted envs: {:?}", merged); + Ok(()) +} + pub fn gen_config_app(config: &user_rootfs_config) -> Result { + // Check config struct length for future possible extension + if config.len != size_of::() { + return_errno!(EINVAL, "User Config Struct length not match"); + } + + // Combine the default envs and user envs if necessary + if !config.envp.is_null() { + combine_trusted_envs(config.envp)?; + } + let upper_layer = to_option_pathbuf(config.upper_layer_path)?; let lower_layer = to_option_pathbuf(config.lower_layer_path)?; let entry_point = to_option_pathbuf(config.entry_point)?;