1 use crate::cli::{StdinStream, StdoutStream, WasiCliCtx}; 2 use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx}; 3 use crate::filesystem::{Dir, WasiFilesystemCtx}; 4 use crate::random::WasiRandomCtx; 5 use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtx}; 6 use crate::{DirPerms, FilePerms, OpenMode}; 7 use cap_rand::RngCore; 8 use cap_std::ambient_authority; 9 use std::future::Future; 10 use std::mem; 11 use std::net::SocketAddr; 12 use std::path::Path; 13 use std::pin::Pin; 14 use tokio::io::{stderr, stdin, stdout}; 15 use wasmtime::Result; 16 17 /// Builder-style structure used to create a [`WasiCtx`]. 18 /// 19 /// This type is used to create a [`WasiCtx`] that is considered per-[`Store`] 20 /// state. The [`build`][WasiCtxBuilder::build] method is used to finish the 21 /// building process and produce a finalized [`WasiCtx`]. 22 /// 23 /// # Examples 24 /// 25 /// ``` 26 /// use wasmtime_wasi::WasiCtx; 27 /// 28 /// let mut wasi = WasiCtx::builder(); 29 /// wasi.arg("./foo.wasm"); 30 /// wasi.arg("--help"); 31 /// wasi.env("FOO", "bar"); 32 /// 33 /// let wasi: WasiCtx = wasi.build(); 34 /// ``` 35 /// 36 /// [`Store`]: wasmtime::Store 37 #[derive(Default)] 38 pub struct WasiCtxBuilder { 39 cli: WasiCliCtx, 40 clocks: WasiClocksCtx, 41 filesystem: WasiFilesystemCtx, 42 random: WasiRandomCtx, 43 sockets: WasiSocketsCtx, 44 built: bool, 45 } 46 47 impl WasiCtxBuilder { 48 /// Creates a builder for a new context with default parameters set. 49 /// 50 /// The current defaults are: 51 /// 52 /// * stdin is closed 53 /// * stdout and stderr eat all input and it doesn't go anywhere 54 /// * no env vars 55 /// * no arguments 56 /// * no preopens 57 /// * clocks use the host implementation of wall/monotonic clocks 58 /// * RNGs are all initialized with random state and suitable generator 59 /// quality to satisfy the requirements of WASI APIs. 60 /// * TCP/UDP are allowed but all addresses are denied by default. 61 /// * `wasi:sockets/ip-name-lookup` is denied by default. 62 /// 63 /// These defaults can all be updated via the various builder configuration 64 /// methods below. new() -> Self65 pub fn new() -> Self { 66 Self::default() 67 } 68 69 /// Provides a custom implementation of stdin to use. 70 /// 71 /// By default stdin is closed but an example of using the host's native 72 /// stdin looks like: 73 /// 74 /// ``` 75 /// use wasmtime_wasi::WasiCtx; 76 /// use wasmtime_wasi::cli::stdin; 77 /// 78 /// let mut wasi = WasiCtx::builder(); 79 /// wasi.stdin(stdin()); 80 /// ``` 81 /// 82 /// Note that inheriting the process's stdin can also be done through 83 /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin). stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self84 pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self { 85 self.cli.stdin = Box::new(stdin); 86 self 87 } 88 89 /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout. stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self90 pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self { 91 self.cli.stdout = Box::new(stdout); 92 self 93 } 94 95 /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr. stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self96 pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self { 97 self.cli.stderr = Box::new(stderr); 98 self 99 } 100 101 /// Configures this context's stdin stream to read the host process's 102 /// stdin. 103 /// 104 /// Note that concurrent reads of stdin can produce surprising results so 105 /// when using this it's typically best to have a single wasm instance in 106 /// the process using this. inherit_stdin(&mut self) -> &mut Self107 pub fn inherit_stdin(&mut self) -> &mut Self { 108 self.stdin(stdin()) 109 } 110 111 /// Configures this context's stdout stream to write to the host process's 112 /// stdout. 113 /// 114 /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) 115 /// multiple instances printing to stdout works well. inherit_stdout(&mut self) -> &mut Self116 pub fn inherit_stdout(&mut self) -> &mut Self { 117 self.stdout(stdout()) 118 } 119 120 /// Configures this context's stderr stream to write to the host process's 121 /// stderr. 122 /// 123 /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) 124 /// multiple instances printing to stderr works well. inherit_stderr(&mut self) -> &mut Self125 pub fn inherit_stderr(&mut self) -> &mut Self { 126 self.stderr(stderr()) 127 } 128 129 /// Configures all of stdin, stdout, and stderr to be inherited from the 130 /// host process. 131 /// 132 /// See [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) for some rationale 133 /// on why this should only be done in situations of 134 /// one-instance-per-process. inherit_stdio(&mut self) -> &mut Self135 pub fn inherit_stdio(&mut self) -> &mut Self { 136 self.inherit_stdin().inherit_stdout().inherit_stderr() 137 } 138 139 /// Configures whether or not blocking operations made through this 140 /// `WasiCtx` are allowed to block the current thread. 141 /// 142 /// WASI is currently implemented on top of the Rust 143 /// [Tokio](https://tokio.rs/) library. While most WASI APIs are 144 /// non-blocking some are instead blocking from the perspective of 145 /// WebAssembly. For example opening a file is a blocking operation with 146 /// respect to WebAssembly but it's implemented as an asynchronous operation 147 /// on the host. This is currently done with Tokio's 148 /// [`spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html). 149 /// 150 /// When WebAssembly is used in a synchronous context then this asynchronous 151 /// operation is quickly turned back into a synchronous operation with a 152 /// `block_on` in Rust. This switching back-and-forth between a blocking a 153 /// non-blocking context can have overhead, and this option exists to help 154 /// alleviate this overhead. 155 /// 156 /// This option indicates that for WASI functions that are blocking from the 157 /// perspective of WebAssembly it's ok to block the native thread as well. 158 /// This means that this back-and-forth between async and sync won't happen 159 /// and instead blocking operations are performed on-thread (such as opening 160 /// a file). This can improve the performance of WASI operations when async 161 /// support is disabled. allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self162 pub fn allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self { 163 self.filesystem.allow_blocking_current_thread = enable; 164 self 165 } 166 167 /// Appends multiple environment variables at once for this builder. 168 /// 169 /// All environment variables are appended to the list of environment 170 /// variables that this builder will configure. 171 /// 172 /// At this time environment variables are not deduplicated and if the same 173 /// key is set twice then the guest will see two entries for the same key. 174 /// 175 /// # Examples 176 /// 177 /// ``` 178 /// use wasmtime_wasi::WasiCtxBuilder; 179 /// 180 /// let mut wasi = WasiCtxBuilder::new(); 181 /// wasi.envs(&[ 182 /// ("FOO", "bar"), 183 /// ("HOME", "/somewhere"), 184 /// ]); 185 /// ``` envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self186 pub fn envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self { 187 self.cli.environment.extend( 188 env.iter() 189 .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())), 190 ); 191 self 192 } 193 194 /// Appends a single environment variable for this builder. 195 /// 196 /// At this time environment variables are not deduplicated and if the same 197 /// key is set twice then the guest will see two entries for the same key. 198 /// 199 /// # Examples 200 /// 201 /// ``` 202 /// use wasmtime_wasi::WasiCtxBuilder; 203 /// 204 /// let mut wasi = WasiCtxBuilder::new(); 205 /// wasi.env("FOO", "bar"); 206 /// ``` env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self207 pub fn env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self { 208 self.cli 209 .environment 210 .push((k.as_ref().to_owned(), v.as_ref().to_owned())); 211 self 212 } 213 214 /// Configures all environment variables to be inherited from the calling 215 /// process into this configuration. 216 /// 217 /// This will use [`envs`](WasiCtxBuilder::envs) to append all host-defined 218 /// environment variables. inherit_env(&mut self) -> &mut Self219 pub fn inherit_env(&mut self) -> &mut Self { 220 self.cli.environment.extend(std::env::vars()); 221 self 222 } 223 224 /// Appends a list of arguments to the argument array to pass to wasm. args(&mut self, args: &[impl AsRef<str>]) -> &mut Self225 pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self { 226 self.cli 227 .arguments 228 .extend(args.iter().map(|a| a.as_ref().to_owned())); 229 self 230 } 231 232 /// Appends a single argument to get passed to wasm. arg(&mut self, arg: impl AsRef<str>) -> &mut Self233 pub fn arg(&mut self, arg: impl AsRef<str>) -> &mut Self { 234 self.cli.arguments.push(arg.as_ref().to_owned()); 235 self 236 } 237 238 /// Appends all host process arguments to the list of arguments to get 239 /// passed to wasm. inherit_args(&mut self) -> &mut Self240 pub fn inherit_args(&mut self) -> &mut Self { 241 self.cli.arguments.extend(std::env::args()); 242 self 243 } 244 245 /// Configures a "preopened directory" to be available to WebAssembly. 246 /// 247 /// By default WebAssembly does not have access to the filesystem because 248 /// there are no preopened directories. All filesystem operations, such as 249 /// opening a file, are done through a preexisting handle. This means that 250 /// to provide WebAssembly access to a directory it must be configured 251 /// through this API. 252 /// 253 /// WASI will also prevent access outside of files provided here. For 254 /// example `..` can't be used to traverse up from the `host_path` provided here 255 /// to the containing directory. 256 /// 257 /// * `host_path` - a path to a directory on the host to open and make 258 /// accessible to WebAssembly. Note that the name of this directory in the 259 /// guest is configured with `guest_path` below. 260 /// * `guest_path` - the name of the preopened directory from WebAssembly's 261 /// perspective. Note that this does not need to match the host's name for 262 /// the directory. 263 /// * `dir_perms` - this is the permissions that wasm will have to operate on 264 /// `guest_path`. This can be used, for example, to provide readonly access to a 265 /// directory. 266 /// * `file_perms` - similar to `dir_perms` but corresponds to the maximum set 267 /// of permissions that can be used for any file in this directory. 268 /// 269 /// # Errors 270 /// 271 /// This method will return an error if `host_path` cannot be opened. 272 /// 273 /// # Examples 274 /// 275 /// ``` 276 /// use wasmtime_wasi::WasiCtxBuilder; 277 /// use wasmtime_wasi::{DirPerms, FilePerms}; 278 /// 279 /// # fn main() {} 280 /// # fn foo() -> wasmtime::Result<()> { 281 /// let mut wasi = WasiCtxBuilder::new(); 282 /// 283 /// // Make `./host-directory` available in the guest as `.` 284 /// wasi.preopened_dir("./host-directory", ".", DirPerms::all(), FilePerms::all()); 285 /// 286 /// // Make `./readonly` available in the guest as `./ro` 287 /// wasi.preopened_dir("./readonly", "./ro", DirPerms::READ, FilePerms::READ); 288 /// # Ok(()) 289 /// # } 290 /// ``` preopened_dir( &mut self, host_path: impl AsRef<Path>, guest_path: impl AsRef<str>, dir_perms: DirPerms, file_perms: FilePerms, ) -> Result<&mut Self>291 pub fn preopened_dir( 292 &mut self, 293 host_path: impl AsRef<Path>, 294 guest_path: impl AsRef<str>, 295 dir_perms: DirPerms, 296 file_perms: FilePerms, 297 ) -> Result<&mut Self> { 298 let dir = cap_std::fs::Dir::open_ambient_dir(host_path.as_ref(), ambient_authority())?; 299 let mut open_mode = OpenMode::empty(); 300 if dir_perms.contains(DirPerms::READ) { 301 open_mode |= OpenMode::READ; 302 } 303 if dir_perms.contains(DirPerms::MUTATE) { 304 open_mode |= OpenMode::WRITE; 305 } 306 self.filesystem.preopens.push(( 307 Dir::new( 308 dir, 309 dir_perms, 310 file_perms, 311 open_mode, 312 self.filesystem.allow_blocking_current_thread, 313 ), 314 guest_path.as_ref().to_owned(), 315 )); 316 Ok(self) 317 } 318 319 /// Set the generator for the `wasi:random/random` number generator to the 320 /// custom generator specified. 321 /// 322 /// Note that contexts have a default RNG configured which is a suitable 323 /// generator for WASI and is configured with a random seed per-context. 324 /// 325 /// Guest code may rely on this random number generator to produce fresh 326 /// unpredictable random data in order to maintain its security invariants, 327 /// and ideally should use the insecure random API otherwise, so using any 328 /// prerecorded or otherwise predictable data may compromise security. secure_random(&mut self, random: impl RngCore + Send + 'static) -> &mut Self329 pub fn secure_random(&mut self, random: impl RngCore + Send + 'static) -> &mut Self { 330 self.random.random = Box::new(random); 331 self 332 } 333 334 /// Configures the generator for `wasi:random/insecure`. 335 /// 336 /// The `insecure_random` generator provided will be used for all randomness 337 /// requested by the `wasi:random/insecure` interface. insecure_random(&mut self, insecure_random: impl RngCore + Send + 'static) -> &mut Self338 pub fn insecure_random(&mut self, insecure_random: impl RngCore + Send + 'static) -> &mut Self { 339 self.random.insecure_random = Box::new(insecure_random); 340 self 341 } 342 343 /// Configures the seed to be returned from `wasi:random/insecure-seed` to 344 /// the specified custom value. 345 /// 346 /// By default this number is randomly generated when a builder is created. insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self347 pub fn insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self { 348 self.random.insecure_random_seed = insecure_random_seed; 349 self 350 } 351 352 /// Configures the maximum len accepted by 353 /// `wasi:random/random.get-random-bytes` and 354 /// `wasi:random/insecure.get-insecure-random-bytes`. Calls with a len 355 /// larger than this limit will trap. 356 /// 357 /// Limited to 64M by default. This limit protects the host implementation 358 /// from memory exhaustion from untrusted guest input. A limit of `u64::MAX` 359 /// is equivalent to no limit, but note that this enables a guest to also 360 /// force the host to attempt an allocation of that size. max_random_size(&mut self, max_size: u64) -> &mut Self361 pub fn max_random_size(&mut self, max_size: u64) -> &mut Self { 362 self.random.max_size = max_size; 363 self 364 } 365 366 /// Configures `wasi:clocks/wall-clock` to use the `clock` specified. 367 /// 368 /// By default the host's wall clock is used. wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self369 pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self { 370 self.clocks.wall_clock = Box::new(clock); 371 self 372 } 373 374 /// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified. 375 /// 376 /// By default the host's monotonic clock is used. monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self377 pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self { 378 self.clocks.monotonic_clock = Box::new(clock); 379 self 380 } 381 382 /// Allow all network addresses accessible to the host. 383 /// 384 /// This method will inherit all network addresses meaning that any address 385 /// can be bound by the guest or connected to by the guest using any 386 /// protocol. 387 /// 388 /// See also [`WasiCtxBuilder::socket_addr_check`]. inherit_network(&mut self) -> &mut Self389 pub fn inherit_network(&mut self) -> &mut Self { 390 self.socket_addr_check(|_, _| Box::pin(async { true })) 391 } 392 393 /// A check that will be called for each socket address that is used. 394 /// 395 /// Returning `true` will permit socket connections to the `SocketAddr`, 396 /// while returning `false` will reject the connection. socket_addr_check<F>(&mut self, check: F) -> &mut Self where F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>> + Send + Sync + 'static,397 pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self 398 where 399 F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>> 400 + Send 401 + Sync 402 + 'static, 403 { 404 self.sockets.socket_addr_check = SocketAddrCheck::new(check); 405 self 406 } 407 408 /// Allow usage of `wasi:sockets/ip-name-lookup` 409 /// 410 /// By default this is disabled. allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self411 pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self { 412 self.sockets.allowed_network_uses.ip_name_lookup = enable; 413 self 414 } 415 416 /// Allow usage of UDP. 417 /// 418 /// This is enabled by default, but can be disabled if UDP should be blanket 419 /// disabled. allow_udp(&mut self, enable: bool) -> &mut Self420 pub fn allow_udp(&mut self, enable: bool) -> &mut Self { 421 self.sockets.allowed_network_uses.udp = enable; 422 self 423 } 424 425 /// Allow usage of TCP 426 /// 427 /// This is enabled by default, but can be disabled if TCP should be blanket 428 /// disabled. allow_tcp(&mut self, enable: bool) -> &mut Self429 pub fn allow_tcp(&mut self, enable: bool) -> &mut Self { 430 self.sockets.allowed_network_uses.tcp = enable; 431 self 432 } 433 434 /// Uses the configured context so far to construct the final [`WasiCtx`]. 435 /// 436 /// Note that each `WasiCtxBuilder` can only be used to "build" once, and 437 /// calling this method twice will panic. 438 /// 439 /// # Panics 440 /// 441 /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be 442 /// used to create only a single [`WasiCtx`]. Repeated usage of this method 443 /// is not allowed and should use a second builder instead. build(&mut self) -> WasiCtx444 pub fn build(&mut self) -> WasiCtx { 445 assert!(!self.built); 446 447 let Self { 448 cli, 449 clocks, 450 filesystem, 451 random, 452 sockets, 453 built: _, 454 } = mem::replace(self, Self::new()); 455 self.built = true; 456 457 WasiCtx { 458 cli, 459 clocks, 460 filesystem, 461 random, 462 sockets, 463 } 464 } 465 /// Builds a WASIp1 context instead of a [`WasiCtx`]. 466 /// 467 /// This method is the same as [`build`](WasiCtxBuilder::build) but it 468 /// creates a [`WasiP1Ctx`] instead. This is intended for use with the 469 /// [`p1`] module of this crate 470 /// 471 /// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx 472 /// [`p1`]: crate::p1 473 /// 474 /// # Panics 475 /// 476 /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be 477 /// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated 478 /// usage of this method is not allowed and should use a second builder 479 /// instead. 480 #[cfg(feature = "p1")] build_p1(&mut self) -> crate::p1::WasiP1Ctx481 pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx { 482 let wasi = self.build(); 483 crate::p1::WasiP1Ctx::new(wasi) 484 } 485 } 486 487 /// Per-[`Store`] state which holds state necessary to implement WASI from this 488 /// crate. 489 /// 490 /// This structure is created through [`WasiCtxBuilder`] and is stored within 491 /// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided 492 /// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`. 493 /// 494 /// Note that this structure itself does not have any accessors, it's here for 495 /// internal use within the `wasmtime-wasi` crate's implementation of 496 /// bindgen-generated traits. 497 /// 498 /// [`Store`]: wasmtime::Store 499 /// 500 /// # Example 501 /// 502 /// ``` 503 /// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder}; 504 /// 505 /// struct MyState { 506 /// ctx: WasiCtx, 507 /// table: ResourceTable, 508 /// } 509 /// 510 /// impl WasiView for MyState { 511 /// fn ctx(&mut self) -> WasiCtxView<'_> { 512 /// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table } 513 /// } 514 /// } 515 /// 516 /// impl MyState { 517 /// fn new() -> MyState { 518 /// let mut wasi = WasiCtxBuilder::new(); 519 /// wasi.arg("./foo.wasm"); 520 /// wasi.arg("--help"); 521 /// wasi.env("FOO", "bar"); 522 /// 523 /// MyState { 524 /// ctx: wasi.build(), 525 /// table: ResourceTable::new(), 526 /// } 527 /// } 528 /// } 529 /// ``` 530 #[derive(Default)] 531 pub struct WasiCtx { 532 pub(crate) cli: WasiCliCtx, 533 pub(crate) clocks: WasiClocksCtx, 534 pub(crate) filesystem: WasiFilesystemCtx, 535 pub(crate) random: WasiRandomCtx, 536 pub(crate) sockets: WasiSocketsCtx, 537 } 538 539 impl WasiCtx { 540 /// Convenience function for calling [`WasiCtxBuilder::new`]. builder() -> WasiCtxBuilder541 pub fn builder() -> WasiCtxBuilder { 542 WasiCtxBuilder::new() 543 } 544 545 /// Returns access to the underlying [`WasiRandomCtx`]. random(&mut self) -> &mut WasiRandomCtx546 pub fn random(&mut self) -> &mut WasiRandomCtx { 547 &mut self.random 548 } 549 550 /// Returns access to the underlying [`WasiClocksCtx`]. clocks(&mut self) -> &mut WasiClocksCtx551 pub fn clocks(&mut self) -> &mut WasiClocksCtx { 552 &mut self.clocks 553 } 554 555 /// Returns access to the underlying [`WasiFilesystemCtx`]. filesystem(&mut self) -> &mut WasiFilesystemCtx556 pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx { 557 &mut self.filesystem 558 } 559 560 /// Returns access to the underlying [`WasiCliCtx`]. cli(&mut self) -> &mut WasiCliCtx561 pub fn cli(&mut self) -> &mut WasiCliCtx { 562 &mut self.cli 563 } 564 565 /// Returns access to the underlying [`WasiSocketsCtx`]. sockets(&mut self) -> &mut WasiSocketsCtx566 pub fn sockets(&mut self) -> &mut WasiSocketsCtx { 567 &mut self.sockets 568 } 569 } 570