Support passing user's envs for runtime boot case

This commit is contained in:
Zheng, Qi 2022-11-23 13:20:29 +08:00 committed by volcano
parent d864542c34
commit ac5d385747
5 changed files with 90 additions and 9 deletions

@ -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
```

@ -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<dyn Error>> {
let args: Vec<String> = 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<dyn Error>> {
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::<user_rootfs_config>(),
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<String, Box<dyn Error>> {

@ -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<Vec<CString>> =
RwLock::new(LIBOS_CONFIG.env.default.clone());
}
pub fn load_config(config_path: &str, expected_mac: &sgx_aes_gcm_128bit_tag_t) -> Result<Config> {
let mut config_file = {
let config_file = SgxFile::open_integrity_only(config_path).map_err(|e| errno!(e))?;

@ -390,8 +390,10 @@ fn merge_env(env: *const *const c_char) -> Result<Vec<CString>> {
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);

@ -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<Option<PathBuf>> {
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<ConfigApp> {
// Check config struct length for future possible extension
if config.len != size_of::<user_rootfs_config>() {
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)?;