From 634aca8b9d8766ca736999f4538e5461130d0d7e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 28 Aug 2019 10:50:59 -0700 Subject: [PATCH] Add WASI serializing test --- Cargo.lock | 1 + Makefile | 1 + lib/wasi-tests/Cargo.toml | 1 + lib/wasi-tests/src/lib.rs | 61 ++++++++++- lib/wasi-tests/tests/wasitests/fd_read.rs | 14 +++ lib/wasi-tests/tests/wasitests/mod.rs | 1 + lib/wasi-tests/wasitests/fd_read.out | 3 + lib/wasi-tests/wasitests/fd_read.rs | 86 +++++++++++++++ lib/wasi-tests/wasitests/fd_read.wasm | Bin 0 -> 80430 bytes lib/wasi/src/lib.rs | 2 +- lib/wasi/src/state/types.rs | 128 ++++++++++++++-------- 11 files changed, 248 insertions(+), 50 deletions(-) create mode 100644 lib/wasi-tests/tests/wasitests/fd_read.rs create mode 100644 lib/wasi-tests/wasitests/fd_read.out create mode 100644 lib/wasi-tests/wasitests/fd_read.rs create mode 100755 lib/wasi-tests/wasitests/fd_read.wasm diff --git a/Cargo.lock b/Cargo.lock index 83301a509..076922596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1742,6 +1742,7 @@ dependencies = [ "wasmer-clif-backend 0.6.0", "wasmer-dev-utils 0.6.0", "wasmer-llvm-backend 0.6.0", + "wasmer-runtime 0.6.0", "wasmer-runtime-core 0.6.0", "wasmer-singlepass-backend 0.6.0", "wasmer-wasi 0.6.0", diff --git a/Makefile b/Makefile index a12d14fe5..da8fc73c3 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ wasitests-llvm: wasitests-setup cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features llvm -- --test-threads=1 wasitests-unit: + cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1 cargo test --manifest-path lib/wasi/Cargo.toml --release wasitests: wasitests-unit wasitests-singlepass wasitests-cranelift wasitests-llvm diff --git a/lib/wasi-tests/Cargo.toml b/lib/wasi-tests/Cargo.toml index 9cf2c37e1..0bfe102bd 100644 --- a/lib/wasi-tests/Cargo.toml +++ b/lib/wasi-tests/Cargo.toml @@ -10,6 +10,7 @@ build = "build/mod.rs" [dependencies] wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" } +wasmer-runtime = { path = "../runtime", version = "0.6.0" } wasmer-wasi = { path = "../wasi", version = "0.6.0" } # hack to get tests to work wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true } diff --git a/lib/wasi-tests/src/lib.rs b/lib/wasi-tests/src/lib.rs index 5df757613..4508e432e 100644 --- a/lib/wasi-tests/src/lib.rs +++ b/lib/wasi-tests/src/lib.rs @@ -1 +1,60 @@ -// nothing to see here +#![cfg(test)] +use wasmer_runtime::{compile, Func}; +use wasmer_runtime_core::vm::Ctx; +use wasmer_wasi::{state::*, *}; + +use std::ffi::c_void; + +#[test] +fn serializing_works() { + let args = vec![ + b"program_name".into_iter().cloned().collect(), + b"arg1".into_iter().cloned().collect(), + ]; + let envs = vec![ + b"PATH=/bin".into_iter().cloned().collect(), + b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(), + ]; + let wasm_binary = include_bytes!("../wasitests/fd_read.wasm"); + let import_object = generate_import_object( + args.clone(), + envs.clone(), + vec![], + vec![( + ".".to_string(), + std::path::PathBuf::from("wasitests/test_fs/hamlet"), + )], + ); + let module = compile(&wasm_binary[..]) + .map_err(|e| format!("Can't compile module: {:?}", e)) + .unwrap(); + + let state_bytes = { + let instance = module.instantiate(&import_object).unwrap(); + + let start: Func<(), ()> = instance.func("_start").unwrap(); + start.call().unwrap(); + let state = get_wasi_state(instance.context()); + + assert_eq!(state.args, args); + assert_eq!(state.envs, envs); + let bytes = state.freeze().unwrap(); + + bytes + }; + + let mut instance = module.instantiate(&import_object).unwrap(); + + let wasi_state = Box::new(WasiState::unfreeze(&state_bytes).unwrap()); + + instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void; + + let second_entry: Func<(), i32> = instance.func("second_entry").unwrap(); + let result = second_entry.call().unwrap(); + assert_eq!(result, true as i32); +} + +#[allow(clippy::mut_from_ref)] +pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState { + unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) } +} diff --git a/lib/wasi-tests/tests/wasitests/fd_read.rs b/lib/wasi-tests/tests/wasitests/fd_read.rs new file mode 100644 index 000000000..2cd68669a --- /dev/null +++ b/lib/wasi-tests/tests/wasitests/fd_read.rs @@ -0,0 +1,14 @@ +#[test] +fn test_fd_read() { + assert_wasi_output!( + "../../wasitests/fd_read.wasm", + "fd_read", + vec![], + vec![( + ".".to_string(), + ::std::path::PathBuf::from("wasitests/test_fs/hamlet") + ),], + vec![], + "../../wasitests/fd_read.out" + ); +} diff --git a/lib/wasi-tests/tests/wasitests/mod.rs b/lib/wasi-tests/tests/wasitests/mod.rs index a1b1d3781..2849ccd5e 100644 --- a/lib/wasi-tests/tests/wasitests/mod.rs +++ b/lib/wasi-tests/tests/wasitests/mod.rs @@ -9,6 +9,7 @@ mod create_dir; mod envvar; mod fd_allocate; mod fd_pread; +mod fd_read; mod fd_sync; mod file_metadata; mod fs_sandbox_test; diff --git a/lib/wasi-tests/wasitests/fd_read.out b/lib/wasi-tests/wasitests/fd_read.out new file mode 100644 index 000000000..f2b9f2169 --- /dev/null +++ b/lib/wasi-tests/wasitests/fd_read.out @@ -0,0 +1,3 @@ +SCENE IV. The Queen's closet. + + Enter QUEEN GERTRUDE and POLO \ No newline at end of file diff --git a/lib/wasi-tests/wasitests/fd_read.rs b/lib/wasi-tests/wasitests/fd_read.rs new file mode 100644 index 000000000..c0a229f85 --- /dev/null +++ b/lib/wasi-tests/wasitests/fd_read.rs @@ -0,0 +1,86 @@ +// Args: +// mapdir: .:wasitests/test_fs/hamlet + +// this program is used in the pause/resume test + +use std::fs; +#[cfg(target_os = "wasi")] +use std::os::wasi::prelude::AsRawFd; +use std::path::PathBuf; + +#[cfg(target_os = "wasi")] +#[repr(C)] +struct WasiIovec { + pub buf: u32, + pub buf_len: u32, +} + +#[cfg(target_os = "wasi")] +#[link(wasm_import_module = "wasi_unstable")] +extern "C" { + fn fd_read(fd: u32, iovs: u32, iovs_len: u32, nread: u32) -> u16; +} + +#[cfg(target_os = "wasi")] +fn read(fd: u32, iovs: &[&mut [u8]]) -> u32 { + let mut nread = 0; + let mut processed_iovs = vec![]; + + for iov in iovs { + processed_iovs.push(WasiIovec { + buf: iov.as_ptr() as usize as u32, + buf_len: iov.len() as u32, + }) + } + + unsafe { + fd_read( + fd, + processed_iovs.as_ptr() as usize as u32, + processed_iovs.len() as u32, + &mut nread as *mut u32 as usize as u32, + ); + } + nread +} + +fn main() { + #[cfg(not(target_os = "wasi"))] + let mut base = PathBuf::from("wasitests/test_fs/hamlet"); + #[cfg(target_os = "wasi")] + let mut base = PathBuf::from("."); + + base.push("act3/scene4.txt"); + let mut file = fs::File::open(&base).expect("Could not open file"); + let mut buffer = [0u8; 64]; + + #[cfg(target_os = "wasi")] + { + let raw_fd = file.as_raw_fd(); + assert_eq!(read(raw_fd, &[&mut buffer]), 64); + let str_val = std::str::from_utf8(&buffer[..]).unwrap().to_string(); + println!("{}", &str_val); + } + // leak the file handle so that we can use it later + std::mem::forget(file); + + #[cfg(not(target_os = "wasi"))] + { + // eh, just print the output directly + print!( + "SCENE IV. The Queen's closet. + + Enter QUEEN GERTRUDE and POLO" + ); + } +} + +#[cfg(target_os = "wasi")] +#[no_mangle] +fn second_entry() -> bool { + let raw_fd = 5; + let mut buffer = [0u8; 8]; + let result = read(raw_fd, &[&mut buffer]); + + &buffer == b"NIUS \n\nL" +} diff --git a/lib/wasi-tests/wasitests/fd_read.wasm b/lib/wasi-tests/wasitests/fd_read.wasm new file mode 100755 index 0000000000000000000000000000000000000000..7cd2891ceab648b88600bd399fa0f95bd23ab5b3 GIT binary patch literal 80430 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 UgwGX4J>>kG``ZlspPGUH0m#>$82|tP literal 0 HcmV?d00001 diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index c5a808874..508766cdb 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -54,7 +54,7 @@ pub fn generate_import_object( }); ( - Box::leak(state) as *mut WasiState as *mut c_void, + Box::into_raw(state) as *mut c_void, state_destructor as fn(*mut c_void), ) }; diff --git a/lib/wasi/src/state/types.rs b/lib/wasi/src/state/types.rs index 1032e2279..94051757e 100644 --- a/lib/wasi/src/state/types.rs +++ b/lib/wasi/src/state/types.rs @@ -1,6 +1,6 @@ /// types for use in the WASI filesystem use crate::syscalls::types::*; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Serialize}; #[cfg(unix)] use std::convert::TryInto; use std::{ @@ -349,61 +349,93 @@ pub struct HostFile { flags: u16, } -struct HostFileVisitor; - -impl<'de> serde::de::Visitor<'de> for HostFileVisitor { - type Value = HostFile; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a HostFile struct") - } - - /*fn visit_bytes(self, v: &[u8]) -> Result { - let host_path = unimplemented!(); - let flags = unimplemented!(); - Ok(HostFile { - inner, - host_path, - flags, - }) - }*/ - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let host_path = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let flags = seq - .next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; - let inner = std::fs::OpenOptions::new() - .read(flags & HostFile::READ != 0) - .write(flags & HostFile::WRITE != 0) - .append(flags & HostFile::APPEND != 0) - .open(&host_path) - .map_err(|_| serde::de::Error::custom("Could not open file on this system"))?; - Ok(HostFile { - inner, - host_path, - flags, - }) - } -} - impl<'de> Deserialize<'de> for HostFile { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_i32(HostFileVisitor) + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + HostPath, + Flags, + } + + struct HostFileVisitor; + + impl<'de> de::Visitor<'de> for HostFileVisitor { + type Value = HostFile; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct HostFile") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: de::SeqAccess<'de>, + { + let host_path = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let flags = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(1, &self))?; + let inner = std::fs::OpenOptions::new() + .read(flags & HostFile::READ != 0) + .write(flags & HostFile::WRITE != 0) + .append(flags & HostFile::APPEND != 0) + .open(&host_path) + .map_err(|_| de::Error::custom("Could not open file on this system"))?; + Ok(HostFile { + inner, + host_path, + flags, + }) + } + + fn visit_map(self, mut map: V) -> Result + where + V: de::MapAccess<'de>, + { + let mut host_path = None; + let mut flags = None; + while let Some(key) = map.next_key()? { + match key { + Field::HostPath => { + if host_path.is_some() { + return Err(de::Error::duplicate_field("host_path")); + } + host_path = Some(map.next_value()?); + } + Field::Flags => { + if flags.is_some() { + return Err(de::Error::duplicate_field("flags")); + } + flags = Some(map.next_value()?); + } + } + } + let host_path = host_path.ok_or_else(|| de::Error::missing_field("host_path"))?; + let flags = flags.ok_or_else(|| de::Error::missing_field("flags"))?; + let inner = std::fs::OpenOptions::new() + .read(flags & HostFile::READ != 0) + .write(flags & HostFile::WRITE != 0) + .append(flags & HostFile::APPEND != 0) + .open(&host_path) + .map_err(|_| de::Error::custom("Could not open file on this system"))?; + Ok(HostFile { + inner, + host_path, + flags, + }) + } + } + + const FIELDS: &'static [&'static str] = &["host_path", "flags"]; + deserializer.deserialize_struct("HostFile", FIELDS, HostFileVisitor) } } -// manually implement Deserialize here such that it uses actual data to open a file; -// I guess we need to add r/w flags and stuff here too.. - impl HostFile { const READ: u16 = 1; const WRITE: u16 = 2;