Skip to main content

wasmtime_wasi/
ctx.rs

1use crate::cli::{StdinStream, StdoutStream, WasiCliCtx};
2use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx};
3use crate::filesystem::{Dir, WasiFilesystemCtx};
4use crate::random::WasiRandomCtx;
5use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtx};
6use crate::{FsPerms, OpenMode};
7use cap_primitives::ambient_authority;
8use rand::Rng;
9use std::future::Future;
10use std::mem;
11use std::net::SocketAddr;
12use std::path::Path;
13use std::pin::Pin;
14use tokio::io::{stderr, stdin, stdout};
15use 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)]
38pub struct WasiCtxBuilder {
39    cli: WasiCliCtx,
40    clocks: WasiClocksCtx,
41    filesystem: WasiFilesystemCtx,
42    random: WasiRandomCtx,
43    sockets: WasiSocketsCtx,
44    built: bool,
45}
46
47impl 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.
65    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).
84    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.
90    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.
96    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.
107    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.
116    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.
125    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.
135    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.
162    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    /// ```
186    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    /// ```
207    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.
219    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.
225    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.
233    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.
240    pub fn inherit_args(&mut self) -> &mut Self {
241        self.cli.arguments.extend(std::env::args());
242        self
243    }
244
245    /// Configures the initial current working directory reported to the guest.
246    ///
247    /// By default no initial current working directory is configured and
248    /// `wasi:cli/environment.initial-cwd` returns `none`.
249    pub fn initial_cwd(&mut self, path: impl AsRef<str>) -> &mut Self {
250        self.cli.initial_cwd = Some(path.as_ref().to_owned());
251        self
252    }
253
254    /// Configures a "preopened directory" to be available to WebAssembly.
255    ///
256    /// By default WebAssembly does not have access to the filesystem because
257    /// there are no preopened directories. All filesystem operations, such as
258    /// opening a file, are done through a preexisting handle. This means that
259    /// to provide WebAssembly access to a directory it must be configured
260    /// through this API.
261    ///
262    /// WASI will also prevent access outside of files provided here. For
263    /// example `..` can't be used to traverse up from the `host_path` provided here
264    /// to the containing directory.
265    ///
266    /// * `host_path` - a path to a directory on the host to open and make
267    ///   accessible to WebAssembly. Note that the name of this directory in the
268    ///   guest is configured with `guest_path` below.
269    /// * `guest_path` - the name of the preopened directory from WebAssembly's
270    ///   perspective. Note that this does not need to match the host's name for
271    ///   the directory.
272    /// * `fs_perms` - permissions enforced by wasmtime-wasi on filesystem
273    ///    operations under a preopen.
274    ///
275    /// # Errors
276    ///
277    /// This method will return an error if `host_path` cannot be opened.
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use wasmtime_wasi::WasiCtxBuilder;
283    /// use wasmtime_wasi::FsPerms;
284    ///
285    /// # fn main() {}
286    /// # fn foo() -> wasmtime::Result<()> {
287    /// let mut wasi = WasiCtxBuilder::new();
288    ///
289    /// // Make `./host-directory` available in the guest as `.`
290    /// wasi.preopened_dir("./host-directory", ".", FsPerms::ReadWrite);
291    ///
292    /// // Make `./readonly` available in the guest as `./ro`
293    /// wasi.preopened_dir("./readonly", "./ro", FsPerms::ReadOnly);
294    /// # Ok(())
295    /// # }
296    /// ```
297    pub fn preopened_dir(
298        &mut self,
299        host_path: impl AsRef<Path>,
300        guest_path: impl AsRef<str>,
301        perms: FsPerms,
302    ) -> Result<&mut Self> {
303        let dir = cap_primitives::fs::open_ambient_dir(host_path.as_ref(), ambient_authority())?;
304        let open_mode = match perms {
305            FsPerms::ReadOnly => OpenMode::READ,
306            FsPerms::ReadWrite => OpenMode::READ | OpenMode::WRITE,
307        };
308        self.filesystem.preopens.push((
309            Dir::new(
310                dir,
311                perms,
312                open_mode,
313                self.filesystem.allow_blocking_current_thread,
314            ),
315            guest_path.as_ref().to_owned(),
316        ));
317        Ok(self)
318    }
319
320    /// Set the generator for the `wasi:random/random` number generator to the
321    /// custom generator specified.
322    ///
323    /// Note that contexts have a default RNG configured which is a suitable
324    /// generator for WASI and is configured with a random seed per-context.
325    ///
326    /// Guest code may rely on this random number generator to produce fresh
327    /// unpredictable random data in order to maintain its security invariants,
328    /// and ideally should use the insecure random API otherwise, so using any
329    /// prerecorded or otherwise predictable data may compromise security.
330    pub fn secure_random(&mut self, random: impl Rng + Send + 'static) -> &mut Self {
331        self.random.random = Box::new(random);
332        self
333    }
334
335    /// Configures the generator for `wasi:random/insecure`.
336    ///
337    /// The `insecure_random` generator provided will be used for all randomness
338    /// requested by the `wasi:random/insecure` interface.
339    pub fn insecure_random(&mut self, insecure_random: impl Rng + Send + 'static) -> &mut Self {
340        self.random.insecure_random = Box::new(insecure_random);
341        self
342    }
343
344    /// Configures the seed to be returned from `wasi:random/insecure-seed` to
345    /// the specified custom value.
346    ///
347    /// By default this number is randomly generated when a builder is created.
348    pub fn insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self {
349        self.random.insecure_random_seed = insecure_random_seed;
350        self
351    }
352
353    /// Configures the maximum len accepted by
354    /// `wasi:random/random.get-random-bytes` and
355    /// `wasi:random/insecure.get-insecure-random-bytes`. Calls with a len
356    /// larger than this limit will trap.
357    ///
358    /// Limited to 64M by default. This limit protects the host implementation
359    /// from memory exhaustion from untrusted guest input. A limit of `u64::MAX`
360    /// is equivalent to no limit, but note that this enables a guest to also
361    /// force the host to attempt an allocation of that size.
362    pub fn max_random_size(&mut self, max_size: u64) -> &mut Self {
363        self.random.max_size = max_size;
364        self
365    }
366
367    /// Configures `wasi:clocks/wall-clock` to use the `clock` specified.
368    ///
369    /// By default the host's wall clock is used.
370    pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self {
371        self.clocks.wall_clock = Box::new(clock);
372        self
373    }
374
375    /// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified.
376    ///
377    /// By default the host's monotonic clock is used.
378    pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self {
379        self.clocks.monotonic_clock = Box::new(clock);
380        self
381    }
382
383    /// Allow all network addresses accessible to the host.
384    ///
385    /// This method will inherit all network addresses meaning that any address
386    /// can be bound by the guest or connected to by the guest using any
387    /// protocol.
388    ///
389    /// See also [`WasiCtxBuilder::socket_addr_check`].
390    pub fn inherit_network(&mut self) -> &mut Self {
391        self.socket_addr_check(|_, _| Box::pin(async { true }))
392    }
393
394    /// A check that will be called for each socket address that is used.
395    ///
396    /// Returning `true` will permit socket connections to the `SocketAddr`,
397    /// while returning `false` will reject the connection.
398    pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self
399    where
400        F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
401            + Send
402            + Sync
403            + 'static,
404    {
405        self.sockets.socket_addr_check = SocketAddrCheck::new(check);
406        self
407    }
408
409    /// Allow usage of `wasi:sockets/ip-name-lookup`
410    ///
411    /// By default this is disabled.
412    pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {
413        self.sockets.allowed_network_uses.ip_name_lookup = enable;
414        self
415    }
416
417    /// Allow usage of UDP
418    ///
419    /// By default this is disabled.
420    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    /// By default this is disabled.
428    pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
429        self.sockets.allowed_network_uses.tcp = enable;
430        self
431    }
432
433    /// Uses the configured context so far to construct the final [`WasiCtx`].
434    ///
435    /// Note that each `WasiCtxBuilder` can only be used to "build" once, and
436    /// calling this method twice will panic.
437    ///
438    /// # Panics
439    ///
440    /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
441    /// used to create only a single [`WasiCtx`]. Repeated usage of this method
442    /// is not allowed and should use a second builder instead.
443    pub fn build(&mut self) -> WasiCtx {
444        assert!(!self.built);
445
446        let Self {
447            cli,
448            clocks,
449            filesystem,
450            random,
451            sockets,
452            built: _,
453        } = mem::replace(self, Self::new());
454        self.built = true;
455
456        WasiCtx {
457            cli,
458            clocks,
459            filesystem,
460            random,
461            sockets,
462        }
463    }
464    /// Builds a WASIp1 context instead of a [`WasiCtx`].
465    ///
466    /// This method is the same as [`build`](WasiCtxBuilder::build) but it
467    /// creates a [`WasiP1Ctx`] instead. This is intended for use with the
468    /// [`p1`] module of this crate
469    ///
470    /// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx
471    /// [`p1`]: crate::p1
472    ///
473    /// # Panics
474    ///
475    /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
476    /// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
477    /// usage of this method is not allowed and should use a second builder
478    /// instead.
479    #[cfg(feature = "p1")]
480    pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {
481        let wasi = self.build();
482        crate::p1::WasiP1Ctx::new(wasi)
483    }
484}
485
486/// Per-[`Store`] state which holds state necessary to implement WASI from this
487/// crate.
488///
489/// This structure is created through [`WasiCtxBuilder`] and is stored within
490/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
491/// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.
492///
493/// Note that this structure itself does not have any accessors, it's here for
494/// internal use within the `wasmtime-wasi` crate's implementation of
495/// bindgen-generated traits.
496///
497/// [`Store`]: wasmtime::Store
498///
499/// # Example
500///
501/// ```
502/// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};
503///
504/// struct MyState {
505///     ctx: WasiCtx,
506///     table: ResourceTable,
507/// }
508///
509/// impl WasiView for MyState {
510///     fn ctx(&mut self) -> WasiCtxView<'_> {
511///         WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
512///     }
513/// }
514///
515/// impl MyState {
516///     fn new() -> MyState {
517///         let mut wasi = WasiCtxBuilder::new();
518///         wasi.arg("./foo.wasm");
519///         wasi.arg("--help");
520///         wasi.env("FOO", "bar");
521///
522///         MyState {
523///             ctx: wasi.build(),
524///             table: ResourceTable::new(),
525///         }
526///     }
527/// }
528/// ```
529#[derive(Default)]
530pub struct WasiCtx {
531    pub(crate) cli: WasiCliCtx,
532    pub(crate) clocks: WasiClocksCtx,
533    pub(crate) filesystem: WasiFilesystemCtx,
534    pub(crate) random: WasiRandomCtx,
535    pub(crate) sockets: WasiSocketsCtx,
536}
537
538impl WasiCtx {
539    /// Convenience function for calling [`WasiCtxBuilder::new`].
540    pub fn builder() -> WasiCtxBuilder {
541        WasiCtxBuilder::new()
542    }
543
544    /// Returns access to the underlying [`WasiRandomCtx`].
545    pub fn random(&mut self) -> &mut WasiRandomCtx {
546        &mut self.random
547    }
548
549    /// Returns access to the underlying [`WasiClocksCtx`].
550    pub fn clocks(&mut self) -> &mut WasiClocksCtx {
551        &mut self.clocks
552    }
553
554    /// Returns access to the underlying [`WasiFilesystemCtx`].
555    pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {
556        &mut self.filesystem
557    }
558
559    /// Returns access to the underlying [`WasiCliCtx`].
560    pub fn cli(&mut self) -> &mut WasiCliCtx {
561        &mut self.cli
562    }
563
564    /// Returns access to the underlying [`WasiSocketsCtx`].
565    pub fn sockets(&mut self) -> &mut WasiSocketsCtx {
566        &mut self.sockets
567    }
568}