diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 7b0cb05df9..f1509a4f46 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -25,7 +25,7 @@ pub fn instantiate( // Additionally register any preopened directories if we have them. let mut builder = wasi_c2::WasiCtx::builder(); - builder.arg(bin_name).arg(".").inherit_stdio(); + builder.arg(bin_name)?.arg(".")?.inherit_stdio(); /* if let Some(workspace) = workspace { diff --git a/crates/wasi-c2/src/ctx.rs b/crates/wasi-c2/src/ctx.rs index acfd8271a6..c8b2ef8434 100644 --- a/crates/wasi-c2/src/ctx.rs +++ b/crates/wasi-c2/src/ctx.rs @@ -1,5 +1,6 @@ use crate::dir::{DirCaps, DirEntry, WasiDir}; use crate::file::{FileCaps, FileEntry, WasiFile}; +use crate::string_array::{StringArray, StringArrayError}; use crate::table::Table; use crate::Error; use std::cell::{RefCell, RefMut}; @@ -7,6 +8,8 @@ use std::path::PathBuf; use std::rc::Rc; pub struct WasiCtx { + pub(crate) args: StringArray, + pub(crate) env: StringArray, table: Rc>, } @@ -17,6 +20,8 @@ impl WasiCtx { pub fn new() -> Self { WasiCtx { + args: StringArray::new(), + env: StringArray::new(), table: Rc::new(RefCell::new(Table::new())), } } @@ -64,9 +69,9 @@ impl WasiCtxBuilder { pub fn build(self) -> Result { Ok(self.0) } - pub fn arg(&mut self, _arg: &str) -> &mut Self { - // Intentionally left blank. We do not handle arguments yet. - self + pub fn arg(&mut self, arg: &str) -> Result<&mut Self, StringArrayError> { + self.0.args.push(arg.to_owned())?; + Ok(self) } pub fn inherit_stdio(&mut self) -> &mut Self { self.0.insert_file( diff --git a/crates/wasi-c2/src/lib.rs b/crates/wasi-c2/src/lib.rs index 44710a5372..82aa0e263a 100644 --- a/crates/wasi-c2/src/lib.rs +++ b/crates/wasi-c2/src/lib.rs @@ -6,9 +6,11 @@ mod error; mod file; pub mod snapshots; pub mod stdio; +mod string_array; pub mod table; pub use ctx::WasiCtx; pub use dir::{DirCaps, WasiDir}; pub use error::Error; pub use file::{FileCaps, WasiFile}; +pub use string_array::StringArrayError; diff --git a/crates/wasi-c2/src/snapshots/preview_1.rs b/crates/wasi-c2/src/snapshots/preview_1.rs index 7de768d2f8..8f53f91135 100644 --- a/crates/wasi-c2/src/snapshots/preview_1.rs +++ b/crates/wasi-c2/src/snapshots/preview_1.rs @@ -106,11 +106,11 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { argv: &GuestPtr<'b, GuestPtr<'b, u8>>, argv_buf: &GuestPtr<'b, u8>, ) -> Result<(), Error> { - unimplemented!() + self.args.write_to_guest(argv_buf, argv) } fn args_sizes_get(&self) -> Result<(types::Size, types::Size), Error> { - unimplemented!() + Ok((self.args.number_elements(), self.args.cumulative_size())) } fn environ_get<'b>( @@ -118,11 +118,11 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { environ: &GuestPtr<'b, GuestPtr<'b, u8>>, environ_buf: &GuestPtr<'b, u8>, ) -> Result<(), Error> { - unimplemented!() + self.env.write_to_guest(environ_buf, environ) } fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), Error> { - unimplemented!() + Ok((self.env.number_elements(), self.env.cumulative_size())) } fn clock_res_get(&self, id: types::Clockid) -> Result { diff --git a/crates/wasi-c2/src/string_array.rs b/crates/wasi-c2/src/string_array.rs new file mode 100644 index 0000000000..8c223e1726 --- /dev/null +++ b/crates/wasi-c2/src/string_array.rs @@ -0,0 +1,71 @@ +use crate::Error; +use wiggle::GuestPtr; + +pub struct StringArray { + elems: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum StringArrayError { + #[error("Number of elements exceeds 2^32")] + NumberElements, + #[error("Element size exceeds 2^32")] + ElementSize, + #[error("Cumulative size exceeds 2^32")] + CumulativeSize, +} + +impl StringArray { + pub fn new() -> Self { + StringArray { elems: Vec::new() } + } + + pub fn push(&mut self, elem: String) -> Result<(), StringArrayError> { + if self.elems.len() + 1 > std::u32::MAX as usize { + return Err(StringArrayError::NumberElements); + } + if elem.as_bytes().len() + 1 > std::u32::MAX as usize { + return Err(StringArrayError::ElementSize); + } + if self.cumulative_size() as usize + elem.as_bytes().len() + 1 > std::u32::MAX as usize { + return Err(StringArrayError::CumulativeSize); + } + self.elems.push(elem); + Ok(()) + } + + pub fn number_elements(&self) -> u32 { + self.elems.len() as u32 + } + + pub fn cumulative_size(&self) -> u32 { + self.elems + .iter() + .map(|e| e.as_bytes().len() + 1) + .sum::() as u32 + } + + pub fn write_to_guest<'a>( + &self, + buffer: &GuestPtr<'a, u8>, + element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>, + ) -> Result<(), Error> { + let element_heads = element_heads.as_array(self.number_elements()); + let buffer = buffer.as_array(self.cumulative_size()); + let mut cursor = 0; + for (elem, head) in self.elems.iter().zip(element_heads.iter()) { + let bytes = elem.as_bytes(); + let len = bytes.len() as u32; + { + let elem_buffer = buffer + .get_range(cursor..(cursor + len)) + .ok_or(Error::Inval)?; // Elements don't fit in buffer provided + elem_buffer.copy_from_slice(bytes)?; + } + buffer.get(cursor + len).ok_or(Error::Inval)?.write(0)?; // 0 terminate + head?.write(buffer.get(cursor).expect("already validated"))?; + cursor += len + 1; + } + Ok(()) + } +}