diff --git a/.appveyor.yml b/.appveyor.yml index 00ccff954..bbba6ac17 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -64,6 +64,7 @@ build_script: - rename target wapm-cli-target - cd .. - move wapm-cli\wapm-cli-target wapm-cli-target + - cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml test_script: - cargo test --manifest-path lib/spectests/Cargo.toml --features clif diff --git a/.circleci/config.yml b/.circleci/config.yml index 520dbc842..eead29af0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -191,6 +191,11 @@ jobs: echo "${CIRCLE_TAG}" >> artifacts/git_version make build-install cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) + - run: + name: Dynamic library + command: | + cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml + cp target/release/libwasmer_runtime_c_api.so ./artifacts - persist_to_workspace: root: . paths: @@ -278,6 +283,13 @@ jobs: cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) # VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) # echo "${VERSION}" >> artifacts/version + - run: + name: Generate dynamic library for the runtime C API + command: | + export PATH="$HOME/.cargo/bin:$PATH" + cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml + install_name_tool -id "@rpath/libwasmer_runtime_c_api.dylib" target/release/libwasmer_runtime_c_api.dylib + cp target/release/libwasmer_runtime_c_api.dylib ./artifacts - persist_to_workspace: root: . paths: @@ -351,7 +363,7 @@ jobs: # VERSION_TAG=$(git describe --exact-match --tags) #if [ "$VERSION" == "$VERSION_TAG" ]; then # echo "Versions match, publishing to Github" - ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -soft ${VERSION} ./artifacts/ + ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${VERSION} ./artifacts/ || true #else # echo "Versions don't match. Wasmer output version (wasmer --version) is ${VERSION} while Git tag is ${VERSION_TAG}" # exit 1 diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e3d87738d..e1d1c4ae3 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2,7 +2,7 @@ Wasmer uses the following components: -- [Cranelift](https://github.com/cranestation/cranelift): for compiling Wasm binaries to machine code +- Compiler backends: for compiling Wasm binaries to machine code ([more info here](https://github.com/wasmerio/wasmer/tree/master/lib#backends)) - [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and running WebAssembly spec tests - [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly modules @@ -67,3 +67,8 @@ Once that's finished, we will have a `Instance` function that will be ready to e Wasmer's Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs. We provide this integration by filling the `import_object` with the Emscripten functions, while instantiating the WebAssembly Instance. + +## WASI + +Wasmer's WASI integration implements all the different syscalls that WASI needs. +We provide this integration by filling the `import_object` with the WASI functions, while instantiating the WebAssembly Instance. diff --git a/CHANGELOG.md b/CHANGELOG.md index 93857f7d8..35838a19b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#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 - [#461](https://github.com/wasmerio/wasmer/pull/461) Prevent passing negative lengths in various places in the runtime C API diff --git a/Cargo.lock b/Cargo.lock index 805ecaf16..2e3e9a949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1412,6 +1412,7 @@ version = "0.4.2" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/README.md b/README.md index d67062125..27b04baab 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ curl https://get.wasmer.io -sSfL | sh Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨: -* [**Rust**](https://github.com/wasmerio/wasmer-rust-example) +* [🦀 **Rust**](https://github.com/wasmerio/wasmer-rust-example) * [**C/C++**](https://github.com/wasmerio/wasmer-c-api) * [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm) * [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm) * [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm) +* [**🐹 Go**](https://github.com/wasmerio/go-ext-wasm) ### Usage diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..11b5c0f13 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +While in beta, the latest published version of `wasmer` (`0.x`) will be supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.x | :white_check_mark: | + +## Reporting a Vulnerability + +The Wasmer team and community take security bugs in Wasmer seriously. +We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +To report a security issue, email security@wasmer.io and include the word "SECURITY" in the subject line. + +The Wasmer team will send a response indicating the next steps in handling your report. +After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index c9209eb8f..3d9e5df71 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -9,14 +9,15 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" } +byteorder = "1" +hashbrown = "0.1" lazy_static = "1.2.0" libc = "0.2.49" -byteorder = "1" time = "0.1.41" wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true } wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.2", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs index 897fb38f0..98bc39dc8 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -10,6 +10,8 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; +use libc::c_char; + use crate::{allocate_on_stack, EmscriptenData}; use std::os::raw::c_int; use wasmer_runtime_core::vm::Ctx; @@ -112,3 +114,41 @@ pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int // TODO: Implement like emscripten expects regarding memory/page size // TODO raise an error } + +pub fn _pathconf(ctx: &mut Ctx, path_addr: c_int, name: c_int) -> c_int { + debug!( + "emscripten::_pathconf {} {} - UNIMPLEMENTED", + path_addr, name + ); + let _path = emscripten_memory_pointer!(ctx.memory(0), path_addr) as *const c_char; + match name { + 0 => 32000, + 1 | 2 | 3 => 255, + 4 | 5 | 16 | 17 | 18 => 4096, + 6 | 7 | 20 => 1, + 8 => 0, + 9 | 10 | 11 | 12 | 14 | 15 | 19 => -1, + 13 => 64, + _ => { + // ___setErrNo(22); + -1 + } + } +} + +pub fn _fpathconf(_ctx: &mut Ctx, _fildes: c_int, name: c_int) -> c_int { + debug!("emscripten::_fpathconf {} {}", _fildes, name); + match name { + 0 => 32000, + 1 | 2 | 3 => 255, + 4 | 5 | 16 | 17 | 18 => 4096, + 6 | 7 | 20 => 1, + 8 => 0, + 9 | 10 | 11 | 12 | 14 | 15 | 19 => -1, + 13 => 64, + _ => { + // ___setErrNo(22); + -1 + } + } +} diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index f60156bab..70d62d950 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -3,8 +3,10 @@ #[macro_use] extern crate wasmer_runtime_core; +use hashbrown::HashMap; use lazy_static::lazy_static; use std::cell::UnsafeCell; +use std::path::PathBuf; use std::{f64, ffi::c_void}; use wasmer_runtime_core::{ error::CallResult, @@ -141,10 +143,14 @@ pub struct EmscriptenData<'a> { pub stack_save: Option>, pub stack_restore: Option>, pub set_threw: Option>, + pub mapped_dirs: HashMap, } impl<'a> EmscriptenData<'a> { - pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> { + pub fn new( + instance: &'a mut Instance, + mapped_dirs: HashMap, + ) -> EmscriptenData<'a> { let malloc = instance.func("_malloc").unwrap(); let free = instance.func("_free").unwrap(); let memalign = instance.func("_memalign").ok(); @@ -272,6 +278,7 @@ impl<'a> EmscriptenData<'a> { stack_save, stack_restore, set_threw, + mapped_dirs, } } } @@ -282,8 +289,9 @@ pub fn run_emscripten_instance( path: &str, args: Vec<&str>, entrypoint: Option, + mapped_dirs: Vec<(String, PathBuf)>, ) -> CallResult<()> { - let mut data = EmscriptenData::new(instance); + let mut data = EmscriptenData::new(instance, mapped_dirs.into_iter().collect()); let data_ptr = &mut data as *mut _ as *mut c_void; instance.context_mut().data = data_ptr; @@ -545,6 +553,8 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_sysconf" => func!(crate::env::_sysconf), "_getaddrinfo" => func!(crate::env::_getaddrinfo), "_times" => func!(crate::env::_times), + "_pathconf" => func!(crate::env::_pathconf), + "_fpathconf" => func!(crate::env::_fpathconf), // Syscalls "___syscall1" => func!(crate::syscalls::___syscall1), @@ -687,6 +697,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_asctime_r" => func!(crate::time::_asctime_r), "_localtime" => func!(crate::time::_localtime), "_time" => func!(crate::time::_time), + "_timegm" => func!(crate::time::_timegm), "_strftime" => func!(crate::time::_strftime), "_strftime_l" => func!(crate::time::_strftime_l), "_localtime_r" => func!(crate::time::_localtime_r), @@ -705,6 +716,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "_llvm_exp2_f32" => func!(crate::math::_llvm_exp2_f32), "_llvm_exp2_f64" => func!(crate::math::_llvm_exp2_f64), "_llvm_trunc_f64" => func!(crate::math::_llvm_trunc_f64), + "_llvm_fma_f64" => func!(crate::math::_llvm_fma_f64), "_emscripten_random" => func!(crate::math::_emscripten_random), // Jump diff --git a/lib/emscripten/src/math.rs b/lib/emscripten/src/math.rs index 3256b49c5..d266afe70 100644 --- a/lib/emscripten/src/math.rs +++ b/lib/emscripten/src/math.rs @@ -49,6 +49,11 @@ pub fn _llvm_trunc_f64(_ctx: &mut Ctx, value: f64) -> f64 { value.trunc() } +pub fn _llvm_fma_f64(_ctx: &mut Ctx, value: f64, a: f64, b: f64) -> f64 { + debug!("emscripten::_llvm_fma_f64"); + value.mul_add(a, b) +} + pub fn _emscripten_random(_ctx: &mut Ctx) -> f64 { debug!("emscripten::_emscripten_random"); -1.0 diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 5654a1299..94f118947 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -10,7 +10,8 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; -use super::utils::copy_stat_into_wasm; +use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory}; + use super::varargs::VarArgs; use byteorder::{ByteOrder, LittleEndian}; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 @@ -38,6 +39,7 @@ use libc::{ // writev, stat, write, + // readlink, }; use wasmer_runtime_core::vm::Ctx; @@ -93,14 +95,20 @@ pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int // chdir pub fn ___syscall12(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall12 (chdir) {}", _which); - let path_addr: i32 = varargs.get(ctx); - unsafe { - let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path_addr) as *const i8; - let _path = std::ffi::CStr::from_ptr(path_ptr); - let ret = chdir(path_ptr); - debug!("=> path: {:?}, ret: {}", _path, ret); + let path_ptr = varargs.get_str(ctx); + let real_path_owned = get_cstr_path(ctx, path_ptr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + path_ptr + }; + let ret = unsafe { chdir(real_path) }; + debug!( + "=> path: {:?}, ret: {}", + unsafe { std::ffi::CStr::from_ptr(real_path) }, ret - } + ); + ret } pub fn ___syscall10(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { @@ -122,15 +130,25 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { // rename pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall38 (rename)"); - let old_path_addr: u32 = varargs.get(ctx); - let new_path_addr: u32 = varargs.get(ctx); - let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8; - let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8; - let result = unsafe { rename(old_path, new_path) }; + let old_path = varargs.get_str(ctx); + let new_path = varargs.get_str(ctx); + let real_old_path_owned = get_cstr_path(ctx, old_path); + let real_old_path = if let Some(ref rp) = real_old_path_owned { + rp.as_c_str().as_ptr() + } else { + old_path + }; + let real_new_path_owned = get_cstr_path(ctx, new_path); + let real_new_path = if let Some(ref rp) = real_new_path_owned { + rp.as_c_str().as_ptr() + } else { + new_path + }; + let result = unsafe { rename(real_old_path, real_new_path) }; debug!( "=> old_path: {}, new_path: {}, result: {}", - unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() }, - unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_old_path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_new_path).to_str().unwrap() }, result ); result @@ -139,9 +157,14 @@ pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { // rmdir pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall40 (rmdir)"); - let pathname: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - unsafe { rmdir(pathname_addr) } + let pathname_addr = varargs.get_str(ctx); + let real_path_owned = get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; + unsafe { rmdir(real_path) } } // pipe @@ -201,9 +224,32 @@ pub fn ___syscall75(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } -pub fn ___syscall85(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall85"); - -1 +// readlink +pub fn ___syscall85(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall85 (readlink)"); + let _path = varargs.get_str(ctx); + let buf = varargs.get_str(ctx); + // let buf_addr: i32 = varargs.get(ctx); + let buf_size: i32 = varargs.get(ctx); + let fd = 3; + let ret = unsafe { read(fd, buf as _, buf_size as _) as i32 }; + debug!( + "=> buf: {}, buf_size: {}, return: {} ", + unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() }, + buf_size, + ret + ); + // let ret = unsafe { + // readlink(path, buf as _, buf_size as _) as i32 + // }; + // debug!("=> path: {}, buf: {}, buf_size: {}, return: {} ", + // unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, + // unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() }, + // // std::ffi::CStr::from_ptr(buf).to_str().unwrap(), + // // buf, + // buf_size, + // ret); + ret } pub fn ___syscall91(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { @@ -224,10 +270,9 @@ pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { // getcwd pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall183"); - use std::env; let buf_offset: c_int = varargs.get(ctx); let _size: c_int = varargs.get(ctx); - let path = env::current_dir(); + let path = get_current_directory(ctx); let path_string = path.unwrap().display().to_string(); let len = path_string.len(); unsafe { @@ -397,19 +442,23 @@ pub fn ___syscall199(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { // stat64 pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall195 (stat64) {}", _which); - let pathname: u32 = varargs.get(ctx); + let pathname_addr = varargs.get_str(ctx); let buf: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; + let real_path_owned = get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; unsafe { let mut _stat: stat = std::mem::zeroed(); - let ret = stat(pathname_addr, &mut _stat); + let ret = stat(real_path, &mut _stat); debug!( - "=> pathname: {}, buf: {}, path: {} = {}\nlast os error: {}", - pathname, + "=> pathname: {}, buf: {} = {}, last os error: {}", + std::ffi::CStr::from_ptr(real_path).to_str().unwrap(), buf, - std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap(), ret, Error::last_os_error() ); @@ -440,11 +489,6 @@ pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in 0 } -pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall220"); - -1 -} - // fcntl64 pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall221 (fcntl64) {}", _which); @@ -457,7 +501,7 @@ pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in // |FNONBLOCK - 0x04 debug!("=> fd: {}, cmd: {}", _fd, cmd); match cmd { - 2 => 0, + 1 | 2 => 0, 13 | 14 => 0, // pretend file locking worked _ => -1, } diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index ec8b98f3f..1c4db071b 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -43,6 +43,7 @@ use libc::{ pid_t, pread, pwrite, + readdir, // readv, recvfrom, recvmsg, @@ -104,19 +105,23 @@ const SO_NOSIGPIPE: c_int = 0; /// open pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall5 (open) {}", _which); - let pathname: u32 = varargs.get(ctx); + let pathname_addr = varargs.get_str(ctx); let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - let _path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; - let fd = unsafe { open(pathname_addr, flags, mode) }; + let real_path_owned = utils::get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; + let _path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }; + let fd = unsafe { open(real_path, flags, mode) }; debug!( - "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}\nlast os error: {}", - pathname, + "=> path: {}, flags: {}, mode: {} = fd: {}, last os error: {}", + _path_str, flags, mode, fd, - _path_str, Error::last_os_error(), ); fd @@ -126,10 +131,8 @@ pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int pub fn ___syscall9(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall9 (link) {}", _which); - let oldname: c_int = varargs.get(ctx); - let newname: c_int = varargs.get(ctx); - let oldname_ptr = emscripten_memory_pointer!(ctx.memory(0), oldname) as *const i8; - let newname_ptr = emscripten_memory_pointer!(ctx.memory(0), newname) as *const i8; + let oldname_ptr = varargs.get_str(ctx); + let newname_ptr = varargs.get_str(ctx); let result = unsafe { link(oldname_ptr, newname_ptr) }; debug!( "=> oldname: {}, newname: {}, result: {}", @@ -156,15 +159,25 @@ pub fn ___syscall77(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int pub fn ___syscall83(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall83 (symlink) {}", _which); - let path1_ptr: c_int = varargs.get(ctx); - let path2_ptr: c_int = varargs.get(ctx); - let path1 = emscripten_memory_pointer!(ctx.memory(0), path1_ptr) as *mut i8; - let path2 = emscripten_memory_pointer!(ctx.memory(0), path2_ptr) as *mut i8; - let result = unsafe { symlink(path1, path2) }; + let path1 = varargs.get_str(ctx); + let path2 = varargs.get_str(ctx); + let real_path1_owned = utils::get_cstr_path(ctx, path1); + let real_path1 = if let Some(ref rp) = real_path1_owned { + rp.as_c_str().as_ptr() + } else { + path1 + }; + let real_path2_owned = utils::get_cstr_path(ctx, path2); + let real_path2 = if let Some(ref rp) = real_path2_owned { + rp.as_c_str().as_ptr() + } else { + path2 + }; + let result = unsafe { symlink(real_path1, real_path2) }; debug!( "=> path1: {}, path2: {}, result: {}", - unsafe { std::ffi::CStr::from_ptr(path1).to_str().unwrap() }, - unsafe { std::ffi::CStr::from_ptr(path2).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_path1).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_path2).to_str().unwrap() }, result, ); result @@ -186,14 +199,19 @@ pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in /// lchown pub fn ___syscall198(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall198 (lchown) {}", _which); - let path: c_int = varargs.get(ctx); + let path_ptr = varargs.get_str(ctx); + let real_path_owned = utils::get_cstr_path(ctx, path_ptr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + path_ptr + }; let uid: uid_t = varargs.get(ctx); let gid: gid_t = varargs.get(ctx); - let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path) as *const i8; - let result = unsafe { lchown(path_ptr, uid, gid) }; + let result = unsafe { lchown(real_path, uid, gid) }; debug!( "=> path: {}, uid: {}, gid: {}, result: {}", - unsafe { std::ffi::CStr::from_ptr(path_ptr).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }, uid, gid, result, @@ -222,13 +240,17 @@ pub fn ___syscall205(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall212 (chown) {}", _which); - let pathname: u32 = varargs.get(ctx); + let pathname_addr = varargs.get_str(ctx); + let real_path_owned = utils::get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; let owner: u32 = varargs.get(ctx); let group: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - - unsafe { chown(pathname_addr, owner, group) } + unsafe { chown(real_path, owner, group) } } /// madvise @@ -247,13 +269,18 @@ pub fn ___syscall219(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in /// access pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall33 (access) {}", _which); - let path_ptr: c_int = varargs.get(ctx); + let path = varargs.get_str(ctx); + let real_path_owned = utils::get_cstr_path(ctx, path); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + path + }; let amode: c_int = varargs.get(ctx); - let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; - let result = unsafe { access(path, amode) }; + let result = unsafe { access(real_path, amode) }; debug!( "=> path: {}, amode: {}, result: {}", - unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }, amode, result ); @@ -270,10 +297,15 @@ pub fn ___syscall34(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int // mkdir pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall39 (mkdir) {}", _which); - let pathname: u32 = varargs.get(ctx); + let pathname_addr = varargs.get_str(ctx); + let real_path_owned = utils::get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; let mode: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - unsafe { mkdir(pathname_addr, mode as _) } + unsafe { mkdir(real_path, mode as _) } } /// dup @@ -771,9 +803,14 @@ pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in /// lstat64 pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall196 (lstat64) {}", _which); - let path_ptr: c_int = varargs.get(ctx); + let path = varargs.get_str(ctx); + let real_path_owned = utils::get_cstr_path(ctx, path); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + path + }; let buf_ptr: u32 = varargs.get(ctx); - let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; unsafe { let mut stat: stat = std::mem::zeroed(); @@ -783,9 +820,9 @@ pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { let stat_ptr = &mut stat as *mut stat; #[cfg(target_os = "macos")] - let ret = lstat64(path, stat_ptr); + let ret = lstat64(real_path, stat_ptr); #[cfg(not(target_os = "macos"))] - let ret = lstat(path, stat_ptr); + let ret = lstat(real_path, stat_ptr); debug!("ret: {}", ret); if ret != 0 { @@ -796,6 +833,45 @@ pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { 0 } +// getdents +// dirent structure is +// i64, i64, u16 (280), i8, [i8; 256] +pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall220"); + let fd: i32 = varargs.get(ctx); + let dirp_addr: i32 = varargs.get(ctx); + let count: u32 = varargs.get(ctx); + + let dirp = emscripten_memory_pointer!(ctx.memory(0), dirp_addr) as *mut u8; + // need to persist stream across calls? + let dir: *mut libc::DIR = unsafe { libc::fdopendir(fd) }; + + let mut pos = 0; + let offset = 280; + while pos + offset <= count as usize { + let dirent = unsafe { readdir(dir) }; + if dirent.is_null() { + break; + } + #[allow(clippy::cast_ptr_alignment)] + unsafe { + *(dirp.add(pos) as *mut u64) = (*dirent).d_ino; + *(dirp.add(pos + 8) as *mut u64) = pos as u64 + offset as u64; + *(dirp.add(pos + 16) as *mut u16) = offset as u16; + *(dirp.add(pos + 18) as *mut u8) = (*dirent).d_type; + let upper_bound = std::cmp::min((*dirent).d_reclen, 254) as usize; + let mut i = 0; + while i < upper_bound { + *(dirp.add(pos + 19 + i) as *mut i8) = (*dirent).d_name[i]; + i += 1; + } + *(dirp.add(pos + 19 + i) as *mut i8) = 0 as i8; + } + pos += offset; + } + pos as i32 +} + /// fallocate pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall324 (fallocate) {}", _which); diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index a7ac8c11c..b3f7b748f 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -1,4 +1,4 @@ -use crate::utils::copy_cstr_into_wasm; +use crate::utils::{copy_cstr_into_wasm, get_cstr_path}; use crate::varargs::VarArgs; use libc::mkdir; use libc::open; @@ -18,11 +18,16 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall5 (open) {}", which); #[cfg(not(feature = "debug"))] let _ = which; - let pathname: u32 = varargs.get(ctx); + let pathname_addr = varargs.get_str(ctx); + let real_path_owned = get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; + let path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }; match path_str { "/dev/urandom" => { // create a fake urandom file for windows, super hacky @@ -43,15 +48,15 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { let fd = unsafe { open(raw_pointer_to_urandom_file, flags, mode) }; debug!( "=> pathname: {}, flags: {}, mode: {} = fd: {}", - pathname, flags, mode, fd + path_str, flags, mode, fd ); fd } _ => { - let fd = unsafe { open(pathname_addr, flags, mode) }; + let fd = unsafe { open(real_path, flags, mode) }; debug!( "=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}", - pathname, flags, mode, fd, path_str + path_str, flags, mode, fd, path_str ); fd } @@ -95,9 +100,14 @@ pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall39 (mkdir) {}", which); #[cfg(not(feature = "debug"))] let _ = which; - let pathname: u32 = varargs.get(ctx); - let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; - unsafe { mkdir(pathname_addr) } + let pathname_addr = varargs.get_str(ctx); + let real_path_owned = get_cstr_path(ctx, pathname_addr); + let real_path = if let Some(ref rp) = real_path_owned { + rp.as_c_str().as_ptr() + } else { + pathname_addr + }; + unsafe { mkdir(real_path) } } /// dup @@ -251,6 +261,12 @@ pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +// getdents +pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall220"); + -1 +} + /// fchown pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall207 (fchown) {}", _which); diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 32e7a61c6..c35d79596 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -4,7 +4,9 @@ use std::mem; use std::time::SystemTime; #[cfg(not(target_os = "windows"))] -use libc::{clockid_t, time as libc_time}; +use libc::{clockid_t, time as libc_time, timegm as libc_timegm, tm as libc_tm}; +#[cfg(not(target_os = "windows"))] +use std::ffi::CString; #[cfg(target_os = "windows")] use libc::time_t; @@ -298,6 +300,57 @@ pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 { } } +/// emscripten: _timegm +#[cfg(not(target_os = "windows"))] +#[allow(clippy::cast_ptr_alignment)] +pub fn _timegm(ctx: &mut Ctx, time_ptr: u32) -> i32 { + debug!("emscripten::_timegm {}", time_ptr); + + unsafe { + let time_p_addr = emscripten_memory_pointer!(ctx.memory(0), time_ptr) as *mut guest_tm; + + let x: *mut c_char = CString::new("").expect("CString::new failed").into_raw(); + let mut rust_tm = libc_tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_gmtoff: 0, + tm_zone: x, + }; + + let result = libc_timegm(&mut rust_tm) as i32; + if result != 0 { + (*time_p_addr).tm_sec = rust_tm.tm_sec; + (*time_p_addr).tm_min = rust_tm.tm_min; + (*time_p_addr).tm_hour = rust_tm.tm_hour; + (*time_p_addr).tm_mday = rust_tm.tm_mday; + (*time_p_addr).tm_mon = rust_tm.tm_mon; + (*time_p_addr).tm_year = rust_tm.tm_year; + (*time_p_addr).tm_wday = rust_tm.tm_wday; + (*time_p_addr).tm_yday = rust_tm.tm_yday; + (*time_p_addr).tm_isdst = rust_tm.tm_isdst; + (*time_p_addr).tm_gmtoff = rust_tm.tm_gmtoff as _; + (*time_p_addr).tm_zone = 0; + } + result + } +} + +#[cfg(target_os = "windows")] +pub fn _timegm(_ctx: &mut Ctx, _time_ptr: c_int) -> i32 { + debug!( + "emscripten::_timegm - UNIMPLEMENTED IN WINDOWS {}", + _time_ptr + ); + -1 +} + /// emscripten: _strftime pub fn _strftime( ctx: &mut Ctx, diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index 2ad5b9a40..112728130 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -5,6 +5,7 @@ use libc::stat; use std::ffi::CStr; use std::mem::size_of; use std::os::raw::c_char; +use std::path::PathBuf; use std::slice; use wasmer_runtime_core::memory::Memory; use wasmer_runtime_core::{ @@ -204,6 +205,61 @@ pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String { String::from_utf8_lossy(&v).to_owned().to_string() } +/// This function trys to find an entry in mapdir +/// translating paths into their correct value +pub fn get_cstr_path(ctx: &mut Ctx, path: *const i8) -> Option { + use std::collections::VecDeque; + + let path_str = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }.to_string(); + let data = get_emscripten_data(ctx); + let path = PathBuf::from(path_str); + let mut prefix_added = false; + let mut components = path.components().collect::>(); + // TODO(mark): handle absolute/non-canonical/non-relative paths too (this + // functionality should be shared among the abis) + if components.len() == 1 { + components.push_front(std::path::Component::CurDir); + prefix_added = true; + } + let mut cumulative_path = PathBuf::new(); + for c in components.into_iter() { + cumulative_path.push(c); + if let Some(val) = data + .mapped_dirs + .get(&cumulative_path.to_string_lossy().to_string()) + { + let rest_of_path = if !prefix_added { + path.strip_prefix(cumulative_path).ok()? + } else { + &path + }; + let rebased_path = val.join(rest_of_path); + return std::ffi::CString::new(rebased_path.to_string_lossy().as_bytes()).ok(); + } + } + None +} + +/// gets the current directory +/// handles mapdir logic +pub fn get_current_directory(ctx: &mut Ctx) -> Option { + if let Some(val) = get_emscripten_data(ctx).mapped_dirs.get(".") { + return Some(val.clone()); + } + std::env::current_dir() + .map(|cwd| { + if let Some(val) = get_emscripten_data(ctx) + .mapped_dirs + .get(&cwd.to_string_lossy().to_string()) + { + val.clone() + } else { + cwd + } + }) + .ok() +} + #[cfg(test)] mod tests { use super::is_emscripten_module; diff --git a/lib/emscripten/src/varargs.rs b/lib/emscripten/src/varargs.rs index a6a1fe46b..8daf78dc8 100644 --- a/lib/emscripten/src/varargs.rs +++ b/lib/emscripten/src/varargs.rs @@ -1,5 +1,7 @@ use std::mem; use wasmer_runtime_core::{types::WasmExternType, vm::Ctx}; +// use std::ffi::CStr; +use std::os::raw::c_char; #[repr(transparent)] #[derive(Copy, Clone)] @@ -13,6 +15,14 @@ impl VarArgs { self.pointer += mem::size_of::() as u32; unsafe { (ptr as *const T).read() } } + + // pub fn getStr<'a>(&mut self, ctx: &mut Ctx) -> &'a CStr { + pub fn get_str(&mut self, ctx: &mut Ctx) -> *const c_char { + let ptr_addr: u32 = self.get(ctx); + let ptr = emscripten_memory_pointer!(ctx.memory(0), ptr_addr) as *const c_char; + ptr + // unsafe { CStr::from_ptr(ptr) } + } } unsafe impl WasmExternType for VarArgs { diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index 43c1aecbf..993842a18 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -56,6 +56,7 @@ macro_rules! assert_emscripten_output { $name, $args, None, + vec![], ).expect("run_emscripten_instance finishes"); let output = capturer.end().unwrap().0; diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 00249ebf9..6f835b4ec 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -445,9 +445,6 @@ fn execute_wasm(options: &Run) -> Result<(), String> { .instantiate(&import_object) .map_err(|e| format!("Can't instantiate module: {:?}", e))?; - if !mapped_dirs.is_empty() { - eprintln!("WARN: mapdir is not implemented for emscripten targets"); - } wasmer_emscripten::run_emscripten_instance( &module, &mut instance, @@ -458,6 +455,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { }, options.args.iter().map(|arg| arg.as_str()).collect(), options.em_entrypoint.clone(), + mapped_dirs, ) .map_err(|e| format!("{:?}", e))?; } else {