wasmtime_wasi/
ctx.rs

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