diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 70d62d950..d260351b9 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -42,6 +42,7 @@ mod lock; mod math; mod memory; mod process; +mod ptr; mod signal; mod storage; mod syscalls; diff --git a/lib/emscripten/src/ptr.rs b/lib/emscripten/src/ptr.rs new file mode 100644 index 000000000..3644fcf9c --- /dev/null +++ b/lib/emscripten/src/ptr.rs @@ -0,0 +1,119 @@ +use std::{cell::Cell, fmt, marker::PhantomData, mem}; +use wasmer_runtime_core::{ + memory::Memory, + types::{ValueType, WasmExternType}, +}; + +pub struct Array; +pub struct Item; + +// TODO: before shipping, refactor this and the wasi code into a common crate +// and wrap it when used in the context of wasi to return the proper error codes + +#[repr(transparent)] +pub struct WasmPtr { + offset: u32, + _phantom: PhantomData<(T, Ty)>, +} + +impl WasmPtr { + #[inline] + pub fn new(offset: u32) -> Self { + Self { + offset, + _phantom: PhantomData, + } + } + + #[inline] + pub fn offset(self) -> i32 { + self.offset as i32 + } +} + +#[inline(always)] +fn align_pointer(ptr: usize, align: usize) -> usize { + // clears bits below aligment amount (assumes power of 2) to align pointer + debug_assert!(align.count_ones() == 1); + ptr & !(align - 1) +} + +impl WasmPtr { + #[inline] + pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell> { + if (self.offset as usize) + mem::size_of::() >= memory.size().bytes().0 { + return None; + } + unsafe { + let cell_ptr = align_pointer( + memory.view::().as_ptr().add(self.offset as usize) as usize, + mem::align_of::(), + ) as *const Cell; + Some(&*cell_ptr) + } + } +} + +impl WasmPtr { + #[inline] + pub fn deref<'a>(self, memory: &'a Memory, index: u32, length: u32) -> Option<&'a [Cell]> { + if (self.offset as usize) + (mem::size_of::() * ((index + length) as usize)) + >= memory.size().bytes().0 + { + return None; + } + + // gets the size of the item in the array with padding added such that + // for any index, we will always result an aligned memory access + let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); + let base_idx = (self.offset as usize) / item_size; + unsafe { + let cell_ptrs = memory + .view::() + .get_unchecked(base_idx + (index as usize)..base_idx + ((index + length) as usize)) + as *const _; + Some(&*cell_ptrs) + } + } +} + +unsafe impl WasmExternType for WasmPtr { + type Native = i32; + + fn to_native(self) -> Self::Native { + self.offset as i32 + } + fn from_native(n: Self::Native) -> Self { + Self { + offset: n as u32, + _phantom: PhantomData, + } + } +} + +unsafe impl ValueType for WasmPtr {} + +impl Clone for WasmPtr { + fn clone(&self) -> Self { + Self { + offset: self.offset, + _phantom: PhantomData, + } + } +} + +impl Copy for WasmPtr {} + +impl PartialEq for WasmPtr { + fn eq(&self, other: &Self) -> bool { + self.offset == other.offset + } +} + +impl Eq for WasmPtr {} + +impl fmt::Debug for WasmPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WasmPtr({:#x})", self.offset) + } +} diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 94f118947..66886feb1 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -10,6 +10,7 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; +use crate::ptr::{Array, WasmPtr}; use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory}; use super::varargs::VarArgs; @@ -77,7 +78,7 @@ pub fn ___syscall3(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall4 (write) {}", _which); let fd: i32 = varargs.get(ctx); - let buf: u32 = varargs.get(ctx); + let buf: i32 = varargs.get(ctx); let count: i32 = varargs.get(ctx); debug!("=> fd: {}, buf: {}, count: {}", fd, buf, count); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *const c_void; @@ -270,21 +271,18 @@ 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"); - let buf_offset: c_int = varargs.get(ctx); + let buf_offset: WasmPtr = varargs.get(ctx); let _size: c_int = varargs.get(ctx); let path = get_current_directory(ctx); let path_string = path.unwrap().display().to_string(); let len = path_string.len(); - unsafe { - let pointer_to_buffer = - emscripten_memory_pointer!(ctx.memory(0), buf_offset) as *mut libc::c_char; - let slice = slice::from_raw_parts_mut(pointer_to_buffer, len.clone()); - for (byte, loc) in path_string.bytes().zip(slice.iter_mut()) { - *loc = byte as _; - } - *pointer_to_buffer.add(len.clone()) = 0; + + let buf_writer = buf_offset.deref(ctx.memory(0), 0, len as u32 + 1).unwrap(); + for (i, byte) in path_string.bytes().enumerate() { + buf_writer[i].set(byte as i8); } - buf_offset + buf_writer[len].set(0); + buf_offset.offset() } // mmap2 diff --git a/lib/wasi/src/ptr.rs b/lib/wasi/src/ptr.rs index 55a16d40c..463e356f1 100644 --- a/lib/wasi/src/ptr.rs +++ b/lib/wasi/src/ptr.rs @@ -29,6 +29,13 @@ impl WasmPtr { } } +#[inline(always)] +fn align_pointer(ptr: usize, align: usize) -> usize { + // clears bits below aligment amount (assumes power of 2) to align pointer + debug_assert!(align.count_ones() == 1); + ptr & !(align - 1) +} + impl WasmPtr { #[inline] pub fn deref<'a>(self, memory: &'a Memory) -> Result<&'a Cell, __wasi_errno_t> { @@ -36,9 +43,7 @@ impl WasmPtr { return Err(__WASI_EFAULT); } unsafe { - // clears bits below aligment amount (assumes power of 2) to align pointer - let aligner = |ptr: usize, align: usize| ptr & !(align - 1); - let cell_ptr = aligner( + let cell_ptr = align_pointer( memory.view::().as_ptr().add(self.offset as usize) as usize, mem::align_of::(), ) as *const Cell; @@ -61,12 +66,15 @@ impl WasmPtr { return Err(__WASI_EFAULT); } + // gets the size of the item in the array with padding added such that + // for any index, we will always result an aligned memory access + let item_size = mem::size_of::() + (mem::size_of::() % mem::align_of::()); + let base_idx = (self.offset as usize) / item_size; unsafe { - let cell_ptrs = memory.view::().get_unchecked( - ((self.offset as usize) / mem::size_of::()) + (index as usize) - ..((self.offset() as usize) / mem::size_of::()) - + ((index + length) as usize), - ) as *const _; + let cell_ptrs = memory + .view::() + .get_unchecked(base_idx + (index as usize)..base_idx + ((index + length) as usize)) + as *const _; Ok(&*cell_ptrs) } }