diff --git a/CHANGELOG.md b/CHANGELOG.md index 35838a19b60..da65b11999d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#476](https://github.com/wasmerio/wasmer/pull/476) Fix bug with wasi::environ_get, fix off by one error in wasi::environ_sizes_get - [#470](https://github.com/wasmerio/wasmer/pull/470) Add mapdir support to Emscripten, implement getdents for Unix - [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API - [#463](https://github.com/wasmerio/wasmer/pull/463) Fix bug in WASI path_open allowing one level above preopened dir to be accessed diff --git a/lib/wasi/build/wasitests.rs b/lib/wasi/build/wasitests.rs index cd3865f5e9f..c305ee44e99 100644 --- a/lib/wasi/build/wasitests.rs +++ b/lib/wasi/build/wasitests.rs @@ -58,6 +58,8 @@ pub fn compile(file: &str, ignores: &HashSet) -> Option { let result = Command::new(&normalized_name) .output() .expect("Failed to execute native program"); + + std::fs::remove_file(&normalized_name).expect("could not delete executable"); let wasm_out_name = format!("{}.wasm", &normalized_name); Command::new("rustc") @@ -76,25 +78,31 @@ pub fn compile(file: &str, ignores: &HashSet) -> Option { }; let src_code = fs::read_to_string(file).expect("read src file"); - let args = extract_args_from_source_file(&src_code); - - let mapdir_args = if let Some(a) = args { - if !a.mapdir.is_empty() { - let mut out_str = String::new(); - out_str.push_str("vec!["); - for (alias, real_dir) in a.mapdir { - out_str.push_str(&format!( - "(\"{}\".to_string(), \"{}\".to_string()),", - alias, real_dir - )); - } - out_str.push_str("]"); - out_str - } else { - "vec![]".to_string() + let args = extract_args_from_source_file(&src_code).unwrap_or_default(); + + let mapdir_args = { + let mut out_str = String::new(); + out_str.push_str("vec!["); + for (alias, real_dir) in args.mapdir { + out_str.push_str(&format!( + "(\"{}\".to_string(), \"{}\".to_string()),", + alias, real_dir + )); } - } else { - "vec![]".to_string() + out_str.push_str("]"); + out_str + }; + + let envvar_args = { + let mut out_str = String::new(); + out_str.push_str("vec!["); + + for entry in args.envvars { + out_str.push_str(&format!("\"{}={}\".to_string(),", entry.0, entry.1)); + } + + out_str.push_str("]"); + out_str }; let contents = format!( @@ -104,6 +112,7 @@ fn test_{rs_module_name}() {{ \"../../{module_path}\", \"{rs_module_name}\", {mapdir_args}, + {envvar_args}, \"../../{test_output_path}\" ); }} @@ -113,6 +122,7 @@ fn test_{rs_module_name}() {{ rs_module_name = rs_module_name, test_output_path = format!("{}.out", normalized_name), mapdir_args = mapdir_args, + envvar_args = envvar_args ); let rust_test_filepath = format!( concat!(env!("CARGO_MANIFEST_DIR"), "/tests/{}.rs"), @@ -167,14 +177,16 @@ fn read_ignore_list() -> HashSet { .collect() } +#[derive(Debug, Default)] struct Args { pub mapdir: Vec<(String, String)>, + pub envvars: Vec<(String, String)>, } /// Pulls args to the program out of a comment at the top of the file starting with "// Args:" fn extract_args_from_source_file(source_code: &str) -> Option { if source_code.starts_with("// Args:") { - let mut args = Args { mapdir: vec![] }; + let mut args = Args::default(); for arg_line in source_code .lines() .skip(1) @@ -196,6 +208,13 @@ fn extract_args_from_source_file(source_code: &str) -> Option { ); } } + "env" => { + if let [name, val] = &tokenized[2].split('=').collect::>()[..] { + args.envvars.push((name.to_string(), val.to_string())); + } else { + eprintln!("Parse error in env {} not parsed correctly", &tokenized[2]); + } + } e => { eprintln!("WARN: comment arg: {} is not supported", e); } diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 51faaacba1a..3f2826bb996 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -195,7 +195,13 @@ pub fn clock_time_get( let memory = ctx.memory(0); let out_addr = wasi_try!(time.deref(memory)); - platform_clock_time_get(clock_id, precision, out_addr) + let result = platform_clock_time_get(clock_id, precision, out_addr); + debug!( + "time: {} => {}", + wasi_try!(time.deref(memory)).get(), + result + ); + result } /// ### `environ_get()` @@ -215,7 +221,7 @@ pub fn environ_get( let state = get_wasi_state(ctx); let memory = ctx.memory(0); - write_buffer_array(memory, &*state.args, environ, environ_buf) + write_buffer_array(memory, &*state.envs, environ, environ_buf) } /// ### `environ_sizes_get()` @@ -238,8 +244,15 @@ pub fn environ_sizes_get( let state = get_wasi_state(ctx); - environ_count.set(state.envs.len() as u32); - environ_buf_size.set(state.envs.iter().map(|v| v.len() as u32).sum()); + let env_var_count = state.envs.len() as u32; + let env_buf_size = state.envs.iter().map(|v| v.len() as u32 + 1).sum(); + environ_count.set(env_var_count); + environ_buf_size.set(env_buf_size); + + debug!( + "env_var_count: {}, env_buf_size: {}", + env_var_count, env_buf_size + ); __WASI_ESUCCESS } @@ -1665,7 +1678,7 @@ pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t { /// - `size_t buf_len` /// The number of bytes that will be written pub fn random_get(ctx: &mut Ctx, buf: WasmPtr, buf_len: u32) -> __wasi_errno_t { - debug!("wasi::random_get"); + debug!("wasi::random_get buf_len: {}", buf_len); let mut rng = thread_rng(); let memory = ctx.memory(0); diff --git a/lib/wasi/tests/wasitests/_common.rs b/lib/wasi/tests/wasitests/_common.rs index 9d01f366f5e..296e88b48ef 100644 --- a/lib/wasi/tests/wasitests/_common.rs +++ b/lib/wasi/tests/wasitests/_common.rs @@ -1,5 +1,5 @@ macro_rules! assert_wasi_output { - ($file:expr, $name:expr, $mapdir_args:expr, $expected:expr) => {{ + ($file:expr, $name:expr, $mapdir_args:expr, $envvar_args:expr, $expected:expr) => {{ use wasmer_dev_utils::stdio::StdioCapturer; use wasmer_runtime_core::{backend::Compiler, Func}; use wasmer_wasi::generate_import_object; diff --git a/lib/wasi/tests/wasitests/create_dir.rs b/lib/wasi/tests/wasitests/create_dir.rs index 1980907f2eb..87a73dc2b4c 100644 --- a/lib/wasi/tests/wasitests/create_dir.rs +++ b/lib/wasi/tests/wasitests/create_dir.rs @@ -5,6 +5,7 @@ fn test_create_dir() { "../../wasitests/create_dir.wasm", "create_dir", vec![], + vec![], "../../wasitests/create_dir.out" ); } diff --git a/lib/wasi/tests/wasitests/envvar.rs b/lib/wasi/tests/wasitests/envvar.rs new file mode 100644 index 00000000000..e54e58f40af --- /dev/null +++ b/lib/wasi/tests/wasitests/envvar.rs @@ -0,0 +1,10 @@ +#[test] +fn test_envvar() { + assert_wasi_output!( + "../../wasitests/envvar.wasm", + "envvar", + vec![], + vec![], + "../../wasitests/envvar.out" + ); +} diff --git a/lib/wasi/tests/wasitests/file_metadata.rs b/lib/wasi/tests/wasitests/file_metadata.rs index a22dc51275e..f0b0b33aabf 100644 --- a/lib/wasi/tests/wasitests/file_metadata.rs +++ b/lib/wasi/tests/wasitests/file_metadata.rs @@ -4,6 +4,7 @@ fn test_file_metadata() { "../../wasitests/file_metadata.wasm", "file_metadata", vec![], + vec![], "../../wasitests/file_metadata.out" ); } diff --git a/lib/wasi/tests/wasitests/fs_sandbox_test.rs b/lib/wasi/tests/wasitests/fs_sandbox_test.rs index f9741f01ffd..6a9c82efa0b 100644 --- a/lib/wasi/tests/wasitests/fs_sandbox_test.rs +++ b/lib/wasi/tests/wasitests/fs_sandbox_test.rs @@ -4,6 +4,7 @@ fn test_fs_sandbox_test() { "../../wasitests/fs_sandbox_test.wasm", "fs_sandbox_test", vec![], + vec![], "../../wasitests/fs_sandbox_test.out" ); } diff --git a/lib/wasi/tests/wasitests/hello.rs b/lib/wasi/tests/wasitests/hello.rs index ffce165e6d7..ec0c0a3519e 100644 --- a/lib/wasi/tests/wasitests/hello.rs +++ b/lib/wasi/tests/wasitests/hello.rs @@ -4,6 +4,7 @@ fn test_hello() { "../../wasitests/hello.wasm", "hello", vec![], + vec![], "../../wasitests/hello.out" ); } diff --git a/lib/wasi/tests/wasitests/mapdir.rs b/lib/wasi/tests/wasitests/mapdir.rs index 005a0b083d9..beddf10c813 100644 --- a/lib/wasi/tests/wasitests/mapdir.rs +++ b/lib/wasi/tests/wasitests/mapdir.rs @@ -4,6 +4,7 @@ fn test_mapdir() { "../../wasitests/mapdir.wasm", "mapdir", vec![], + vec![], "../../wasitests/mapdir.out" ); } diff --git a/lib/wasi/tests/wasitests/mod.rs b/lib/wasi/tests/wasitests/mod.rs index 0da7447af21..362573068f9 100644 --- a/lib/wasi/tests/wasitests/mod.rs +++ b/lib/wasi/tests/wasitests/mod.rs @@ -6,6 +6,7 @@ #[macro_use] mod _common; mod create_dir; +mod envvar; mod file_metadata; mod fs_sandbox_test; mod hello; diff --git a/lib/wasi/tests/wasitests/quine.rs b/lib/wasi/tests/wasitests/quine.rs index 156ded8a486..193b2c19e4b 100644 --- a/lib/wasi/tests/wasitests/quine.rs +++ b/lib/wasi/tests/wasitests/quine.rs @@ -4,6 +4,7 @@ fn test_quine() { "../../wasitests/quine.wasm", "quine", vec![], + vec![], "../../wasitests/quine.out" ); } diff --git a/lib/wasi/wasitests/create_dir b/lib/wasi/wasitests/create_dir deleted file mode 100755 index df15914c965..00000000000 Binary files a/lib/wasi/wasitests/create_dir and /dev/null differ diff --git a/lib/wasi/wasitests/create_dir.wasm b/lib/wasi/wasitests/create_dir.wasm index f7f86a5878e..a8ec3cb5a75 100755 Binary files a/lib/wasi/wasitests/create_dir.wasm and b/lib/wasi/wasitests/create_dir.wasm differ diff --git a/lib/wasi/wasitests/envvar.out b/lib/wasi/wasitests/envvar.out new file mode 100644 index 00000000000..67848f4f488 --- /dev/null +++ b/lib/wasi/wasitests/envvar.out @@ -0,0 +1,6 @@ +Env vars: +CAT=2 +DOG=1 +DOG Ok("1") +DOG_TYPE Err(NotPresent) +SET VAR Ok("HELLO") diff --git a/lib/wasi/wasitests/envvar.rs b/lib/wasi/wasitests/envvar.rs new file mode 100644 index 00000000000..a5d8982c67c --- /dev/null +++ b/lib/wasi/wasitests/envvar.rs @@ -0,0 +1,38 @@ +// Args: +// env: DOG=1 +// env: CAT=2 + +use std::env; + +fn get_env_var(var_name: &str) -> Result { + #[cfg(not(target = "wasi"))] + match var_name { + "DOG" => Ok("1".to_string()), + "CAT" => Ok("2".to_string()), + _ => Err(env::VarError::NotPresent), + } + #[cfg(target = "wasi")] + env::var(var_name) +} + +fn main() { + #[cfg(not(target = "wasi"))] + let mut env_vars = vec!["DOG=1".to_string(), "CAT=2".to_string()]; + #[cfg(target = "wasi")] + let mut env_vars = env::vars() + .map(|(key, value)| format!("{}={}", key, value)) + .collect::>(); + + env_vars.sort(); + + println!("Env vars:"); + for e in env_vars { + println!("{}", e); + } + + env::set_var("WASI_ENVVAR_TEST", "HELLO"); + + println!("DOG {:?}", get_env_var("DOG")); + println!("DOG_TYPE {:?}", get_env_var("DOG_TYPE")); + println!("SET VAR {:?}", env::var("WASI_ENVVAR_TEST")); +} diff --git a/lib/wasi/wasitests/envvar.wasm b/lib/wasi/wasitests/envvar.wasm new file mode 100755 index 00000000000..14bf029962d Binary files /dev/null and b/lib/wasi/wasitests/envvar.wasm differ diff --git a/lib/wasi/wasitests/file_metadata b/lib/wasi/wasitests/file_metadata deleted file mode 100755 index 804d38dd4cc..00000000000 Binary files a/lib/wasi/wasitests/file_metadata and /dev/null differ diff --git a/lib/wasi/wasitests/file_metadata.wasm b/lib/wasi/wasitests/file_metadata.wasm index 3b8b4aae1bf..bc6fe76fff5 100755 Binary files a/lib/wasi/wasitests/file_metadata.wasm and b/lib/wasi/wasitests/file_metadata.wasm differ diff --git a/lib/wasi/wasitests/fs_sandbox_test b/lib/wasi/wasitests/fs_sandbox_test deleted file mode 100755 index eeddb7b0c4a..00000000000 Binary files a/lib/wasi/wasitests/fs_sandbox_test and /dev/null differ diff --git a/lib/wasi/wasitests/fs_sandbox_test.wasm b/lib/wasi/wasitests/fs_sandbox_test.wasm index 31c02714a68..e3b55f0c1c4 100755 Binary files a/lib/wasi/wasitests/fs_sandbox_test.wasm and b/lib/wasi/wasitests/fs_sandbox_test.wasm differ diff --git a/lib/wasi/wasitests/hello b/lib/wasi/wasitests/hello deleted file mode 100755 index 702ef3a4789..00000000000 Binary files a/lib/wasi/wasitests/hello and /dev/null differ diff --git a/lib/wasi/wasitests/hello.wasm b/lib/wasi/wasitests/hello.wasm index fa48455cdd2..58114e759ef 100755 Binary files a/lib/wasi/wasitests/hello.wasm and b/lib/wasi/wasitests/hello.wasm differ diff --git a/lib/wasi/wasitests/mapdir b/lib/wasi/wasitests/mapdir deleted file mode 100755 index 5be49b5601e..00000000000 Binary files a/lib/wasi/wasitests/mapdir and /dev/null differ diff --git a/lib/wasi/wasitests/mapdir.wasm b/lib/wasi/wasitests/mapdir.wasm index c5ebd6d9ab1..a4f32480d3e 100755 Binary files a/lib/wasi/wasitests/mapdir.wasm and b/lib/wasi/wasitests/mapdir.wasm differ diff --git a/lib/wasi/wasitests/quine b/lib/wasi/wasitests/quine deleted file mode 100755 index 6593dbb697c..00000000000 Binary files a/lib/wasi/wasitests/quine and /dev/null differ diff --git a/lib/wasi/wasitests/quine.wasm b/lib/wasi/wasitests/quine.wasm index d11d03addbf..45d739f0403 100755 Binary files a/lib/wasi/wasitests/quine.wasm and b/lib/wasi/wasitests/quine.wasm differ diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 6f835b4ecfb..021a7eaf0d8 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -101,6 +101,10 @@ struct Run { #[structopt(long = "mapdir", multiple = true)] mapped_dirs: Vec, + /// Pass custom environment variables + #[structopt(long = "env", multiple = true)] + env_vars: Vec, + /// Custom code loader #[structopt( long = "loader", @@ -223,6 +227,47 @@ fn get_cache_dir() -> PathBuf { } } +fn get_mapped_dirs(input: &[String]) -> Result, String> { + let mut md = vec![]; + for entry in input.iter() { + if let [alias, real_dir] = entry.split(':').collect::>()[..] { + let pb = PathBuf::from(&real_dir); + if let Ok(pb_metadata) = pb.metadata() { + if !pb_metadata.is_dir() { + return Err(format!( + "\"{}\" exists, but it is not a directory", + &real_dir + )); + } + } else { + return Err(format!("Directory \"{}\" does not exist", &real_dir)); + } + md.push((alias.to_string(), pb)); + continue; + } + return Err(format!( + "Directory mappings must consist of two paths separate by a colon. Found {}", + &entry + )); + } + Ok(md) +} + +fn get_env_var_args(input: &[String]) -> Result, String> { + let mut ev = vec![]; + for entry in input.iter() { + if let [env_var, value] = entry.split('=').collect::>()[..] { + ev.push((env_var, value)); + } else { + return Err(format!( + "Env vars must be of the form =. Found {}", + &entry + )); + } + } + Ok(ev) +} + /// Execute a wasm/wat file fn execute_wasm(options: &Run) -> Result<(), String> { // force disable caching on windows @@ -231,31 +276,8 @@ fn execute_wasm(options: &Run) -> Result<(), String> { #[cfg(not(target_os = "windows"))] let disable_cache = options.disable_cache; - let mapped_dirs = { - let mut md = vec![]; - for entry in options.mapped_dirs.iter() { - if let &[alias, real_dir] = &entry.split(':').collect::>()[..] { - let pb = PathBuf::from(&real_dir); - if let Ok(pb_metadata) = pb.metadata() { - if !pb_metadata.is_dir() { - return Err(format!( - "\"{}\" exists, but it is not a directory", - &real_dir - )); - } - } else { - return Err(format!("Directory \"{}\" does not exist", &real_dir)); - } - md.push((alias.to_string(), pb)); - continue; - } - return Err(format!( - "Directory mappings must consist of two paths separate by a colon. Found {}", - &entry - )); - } - md - }; + let mapped_dirs = get_mapped_dirs(&options.mapped_dirs[..])?; + let env_vars = get_env_var_args(&options.env_vars[..])?; let wasm_path = &options.path; let mut wasm_binary: Vec = read_file_contents(wasm_path).map_err(|err| { @@ -282,9 +304,10 @@ fn execute_wasm(options: &Run) -> Result<(), String> { let num_str = if let Some(ns) = split.next() { ns } else { - return Err(format!( + return Err( "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" - )); + .to_string(), + ); }; let num: u32 = num_str.parse::().map_err(|err| { format!( @@ -295,9 +318,10 @@ fn execute_wasm(options: &Run) -> Result<(), String> { let name_str: String = if let Some(name_str) = split.next() { name_str } else { - return Err(format!( + return Err( "Can't parse symbol map (expected each entry to be of the form: `0:func_name`)" - )); + .to_string(), + ); } .to_owned(); @@ -375,7 +399,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { // cache.load will return the Module if it's able to deserialize it properly, and an error if: // * The file is not found // * The file exists, but it's corrupted or can't be converted to a module - let module = match cache.load(hash) { + match cache.load(hash) { Ok(module) => { // We are able to load the module from cache module @@ -395,8 +419,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { module } - }; - module + } }; if let Some(loader) = options.loader { @@ -471,7 +494,8 @@ fn execute_wasm(options: &Run) -> Result<(), String> { .cloned() .map(|arg| arg.into_bytes()) .collect(), - env::vars() + env_vars + .into_iter() .map(|(k, v)| format!("{}={}", k, v).into_bytes()) .collect(), options.pre_opened_directories.clone(),