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 rand::Rng;
8use std::future::Future;
9use std::mem;
10use std::net::SocketAddr;
11use std::num::NonZeroUsize;
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    /// Configures the maximum number of bytes that a single read or write of a
168    /// file or socket may transfer.
169    ///
170    /// This bounds the size of the host-side buffer allocated for each
171    /// individual filesystem or socket I/O operation, which is how much data a
172    /// guest can cause the host to allocate at once. For example file
173    /// reads/writes, UDP bytes received, and TCP reads/writes will use this
174    /// value when limiting a buffering being allocated is appropriate.
175    ///
176    /// Larger values may improve throughput of large transfers at the cost of
177    /// larger per-operation allocations.
178    ///
179    /// The default is 64 KiB.
180    pub fn max_io_size(&mut self, size: NonZeroUsize) -> &mut Self {
181        self.filesystem.max_io_size = size;
182        self.sockets.max_io_size = size;
183        self
184    }
185
186    /// Appends multiple environment variables at once for this builder.
187    ///
188    /// All environment variables are appended to the list of environment
189    /// variables that this builder will configure.
190    ///
191    /// At this time environment variables are not deduplicated and if the same
192    /// key is set twice then the guest will see two entries for the same key.
193    ///
194    /// # Examples
195    ///
196    /// ```
197    /// use wasmtime_wasi::WasiCtxBuilder;
198    ///
199    /// let mut wasi = WasiCtxBuilder::new();
200    /// wasi.envs(&[
201    ///     ("FOO", "bar"),
202    ///     ("HOME", "/somewhere"),
203    /// ]);
204    /// ```
205    pub fn envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self {
206        self.cli.environment.extend(
207            env.iter()
208                .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())),
209        );
210        self
211    }
212
213    /// Appends a single environment variable for this builder.
214    ///
215    /// At this time environment variables are not deduplicated and if the same
216    /// key is set twice then the guest will see two entries for the same key.
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use wasmtime_wasi::WasiCtxBuilder;
222    ///
223    /// let mut wasi = WasiCtxBuilder::new();
224    /// wasi.env("FOO", "bar");
225    /// ```
226    pub fn env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self {
227        self.cli
228            .environment
229            .push((k.as_ref().to_owned(), v.as_ref().to_owned()));
230        self
231    }
232
233    /// Configures all environment variables to be inherited from the calling
234    /// process into this configuration.
235    ///
236    /// This will use [`envs`](WasiCtxBuilder::envs) to append all host-defined
237    /// environment variables.
238    pub fn inherit_env(&mut self) -> &mut Self {
239        self.cli.environment.extend(std::env::vars());
240        self
241    }
242
243    /// Appends a list of arguments to the argument array to pass to wasm.
244    pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {
245        self.cli
246            .arguments
247            .extend(args.iter().map(|a| a.as_ref().to_owned()));
248        self
249    }
250
251    /// Appends a single argument to get passed to wasm.
252    pub fn arg(&mut self, arg: impl AsRef<str>) -> &mut Self {
253        self.cli.arguments.push(arg.as_ref().to_owned());
254        self
255    }
256
257    /// Appends all host process arguments to the list of arguments to get
258    /// passed to wasm.
259    pub fn inherit_args(&mut self) -> &mut Self {
260        self.cli.arguments.extend(std::env::args());
261        self
262    }
263
264    /// Configures the initial current working directory reported to the guest.
265    ///
266    /// By default no initial current working directory is configured and
267    /// `wasi:cli/environment.initial-cwd` returns `none`.
268    pub fn initial_cwd(&mut self, path: impl AsRef<str>) -> &mut Self {
269        self.cli.initial_cwd = Some(path.as_ref().to_owned());
270        self
271    }
272
273    /// Configures a "preopened directory" to be available to WebAssembly.
274    ///
275    /// By default WebAssembly does not have access to the filesystem because
276    /// there are no preopened directories. All filesystem operations, such as
277    /// opening a file, are done through a preexisting handle. This means that
278    /// to provide WebAssembly access to a directory it must be configured
279    /// through this API.
280    ///
281    /// WASI will also prevent access outside of files provided here. For
282    /// example `..` can't be used to traverse up from the `host_path` provided here
283    /// to the containing directory.
284    ///
285    /// * `host_path` - a path to a directory on the host to open and make
286    ///   accessible to WebAssembly. Note that the name of this directory in the
287    ///   guest is configured with `guest_path` below.
288    /// * `guest_path` - the name of the preopened directory from WebAssembly's
289    ///   perspective. Note that this does not need to match the host's name for
290    ///   the directory.
291    /// * `fs_perms` - permissions enforced by wasmtime-wasi on filesystem
292    ///    operations under a preopen.
293    ///
294    /// # Errors
295    ///
296    /// This method will return an error if `host_path` cannot be opened.
297    ///
298    /// # Examples
299    ///
300    /// ```
301    /// use wasmtime_wasi::WasiCtxBuilder;
302    /// use wasmtime_wasi::FsPerms;
303    ///
304    /// # fn main() {}
305    /// # fn foo() -> wasmtime::Result<()> {
306    /// let mut wasi = WasiCtxBuilder::new();
307    ///
308    /// // Make `./host-directory` available in the guest as `.`
309    /// wasi.preopened_dir("./host-directory", ".", FsPerms::ReadWrite);
310    ///
311    /// // Make `./readonly` available in the guest as `./ro`
312    /// wasi.preopened_dir("./readonly", "./ro", FsPerms::ReadOnly);
313    /// # Ok(())
314    /// # }
315    /// ```
316    pub fn preopened_dir(
317        &mut self,
318        host_path: impl AsRef<Path>,
319        guest_path: impl AsRef<str>,
320        perms: FsPerms,
321    ) -> Result<&mut Self> {
322        let dir = crate::filesystem::primitives::open_ambient_dir(host_path.as_ref())?;
323        let open_mode = match perms {
324            FsPerms::ReadOnly => OpenMode::READ,
325            FsPerms::ReadWrite => OpenMode::READ | OpenMode::WRITE,
326        };
327        self.filesystem.preopens.push((
328            Dir::new(
329                dir,
330                perms,
331                open_mode,
332                self.filesystem.allow_blocking_current_thread,
333            ),
334            guest_path.as_ref().to_owned(),
335        ));
336        Ok(self)
337    }
338
339    /// Set the generator for the `wasi:random/random` number generator to the
340    /// custom generator specified.
341    ///
342    /// Note that contexts have a default RNG configured which is a suitable
343    /// generator for WASI and is configured with a random seed per-context.
344    ///
345    /// Guest code may rely on this random number generator to produce fresh
346    /// unpredictable random data in order to maintain its security invariants,
347    /// and ideally should use the insecure random API otherwise, so using any
348    /// prerecorded or otherwise predictable data may compromise security.
349    pub fn secure_random(&mut self, random: impl Rng + Send + 'static) -> &mut Self {
350        self.random.random = Box::new(random);
351        self
352    }
353
354    /// Configures the generator for `wasi:random/insecure`.
355    ///
356    /// The `insecure_random` generator provided will be used for all randomness
357    /// requested by the `wasi:random/insecure` interface.
358    pub fn insecure_random(&mut self, insecure_random: impl Rng + Send + 'static) -> &mut Self {
359        self.random.insecure_random = Box::new(insecure_random);
360        self
361    }
362
363    /// Configures the seed to be returned from `wasi:random/insecure-seed` to
364    /// the specified custom value.
365    ///
366    /// By default this number is randomly generated when a builder is created.
367    pub fn insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self {
368        self.random.insecure_random_seed = insecure_random_seed;
369        self
370    }
371
372    /// Configures the maximum len accepted by
373    /// `wasi:random/random.get-random-bytes` and
374    /// `wasi:random/insecure.get-insecure-random-bytes`. Calls with a len
375    /// larger than this limit will trap.
376    ///
377    /// Limited to 64M by default. This limit protects the host implementation
378    /// from memory exhaustion from untrusted guest input. A limit of `u64::MAX`
379    /// is equivalent to no limit, but note that this enables a guest to also
380    /// force the host to attempt an allocation of that size.
381    pub fn max_random_size(&mut self, max_size: u64) -> &mut Self {
382        self.random.max_size = max_size;
383        self
384    }
385
386    /// Configures `wasi:clocks/wall-clock` to use the `clock` specified.
387    ///
388    /// By default the host's wall clock is used.
389    pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self {
390        self.clocks.wall_clock = Box::new(clock);
391        self
392    }
393
394    /// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified.
395    ///
396    /// By default the host's monotonic clock is used.
397    pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self {
398        self.clocks.monotonic_clock = Box::new(clock);
399        self
400    }
401
402    /// Allow all network addresses accessible to the host.
403    ///
404    /// This method will inherit all network addresses meaning that any address
405    /// can be bound by the guest or connected to by the guest using any
406    /// protocol.
407    ///
408    /// See also [`WasiCtxBuilder::socket_addr_check`].
409    pub fn inherit_network(&mut self) -> &mut Self {
410        self.socket_addr_check(|_, _| Box::pin(async { true }))
411    }
412
413    /// A check that will be called for each socket address that is used.
414    ///
415    /// Returning `true` will permit socket connections to the `SocketAddr`,
416    /// while returning `false` will reject the connection.
417    pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self
418    where
419        F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
420            + Send
421            + Sync
422            + 'static,
423    {
424        self.sockets.socket_addr_check = SocketAddrCheck::new(check);
425        self
426    }
427
428    /// Allow usage of `wasi:sockets/ip-name-lookup`
429    ///
430    /// By default this is disabled.
431    pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {
432        self.sockets.allowed_network_uses.ip_name_lookup = enable;
433        self
434    }
435
436    /// Allow usage of UDP
437    ///
438    /// By default this is disabled.
439    pub fn allow_udp(&mut self, enable: bool) -> &mut Self {
440        self.sockets.allowed_network_uses.udp = enable;
441        self
442    }
443
444    /// Allow usage of TCP
445    ///
446    /// By default this is disabled.
447    pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
448        self.sockets.allowed_network_uses.tcp = enable;
449        self
450    }
451
452    /// Uses the configured context so far to construct the final [`WasiCtx`].
453    ///
454    /// Note that each `WasiCtxBuilder` can only be used to "build" once, and
455    /// calling this method twice will panic.
456    ///
457    /// # Panics
458    ///
459    /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
460    /// used to create only a single [`WasiCtx`]. Repeated usage of this method
461    /// is not allowed and should use a second builder instead.
462    pub fn build(&mut self) -> WasiCtx {
463        assert!(!self.built);
464
465        let Self {
466            cli,
467            clocks,
468            filesystem,
469            random,
470            sockets,
471            built: _,
472        } = mem::replace(self, Self::new());
473        self.built = true;
474
475        WasiCtx {
476            cli,
477            clocks,
478            filesystem,
479            random,
480            sockets,
481        }
482    }
483    /// Builds a WASIp1 context instead of a [`WasiCtx`].
484    ///
485    /// This method is the same as [`build`](WasiCtxBuilder::build) but it
486    /// creates a [`WasiP1Ctx`] instead. This is intended for use with the
487    /// [`p1`] module of this crate
488    ///
489    /// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx
490    /// [`p1`]: crate::p1
491    ///
492    /// # Panics
493    ///
494    /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
495    /// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
496    /// usage of this method is not allowed and should use a second builder
497    /// instead.
498    #[cfg(feature = "p1")]
499    pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {
500        let wasi = self.build();
501        crate::p1::WasiP1Ctx::new(wasi)
502    }
503}
504
505/// Per-[`Store`] state which holds state necessary to implement WASI from this
506/// crate.
507///
508/// This structure is created through [`WasiCtxBuilder`] and is stored within
509/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
510/// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.
511///
512/// Note that this structure itself does not have any accessors, it's here for
513/// internal use within the `wasmtime-wasi` crate's implementation of
514/// bindgen-generated traits.
515///
516/// [`Store`]: wasmtime::Store
517///
518/// # Example
519///
520/// ```
521/// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};
522///
523/// struct MyState {
524///     ctx: WasiCtx,
525///     table: ResourceTable,
526/// }
527///
528/// impl WasiView for MyState {
529///     fn ctx(&mut self) -> WasiCtxView<'_> {
530///         WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
531///     }
532/// }
533///
534/// impl MyState {
535///     fn new() -> MyState {
536///         let mut wasi = WasiCtxBuilder::new();
537///         wasi.arg("./foo.wasm");
538///         wasi.arg("--help");
539///         wasi.env("FOO", "bar");
540///
541///         MyState {
542///             ctx: wasi.build(),
543///             table: ResourceTable::new(),
544///         }
545///     }
546/// }
547/// ```
548#[derive(Default)]
549pub struct WasiCtx {
550    pub(crate) cli: WasiCliCtx,
551    pub(crate) clocks: WasiClocksCtx,
552    pub(crate) filesystem: WasiFilesystemCtx,
553    pub(crate) random: WasiRandomCtx,
554    pub(crate) sockets: WasiSocketsCtx,
555}
556
557impl WasiCtx {
558    /// Convenience function for calling [`WasiCtxBuilder::new`].
559    pub fn builder() -> WasiCtxBuilder {
560        WasiCtxBuilder::new()
561    }
562
563    /// Returns access to the underlying [`WasiRandomCtx`].
564    pub fn random(&mut self) -> &mut WasiRandomCtx {
565        &mut self.random
566    }
567
568    /// Returns access to the underlying [`WasiClocksCtx`].
569    pub fn clocks(&mut self) -> &mut WasiClocksCtx {
570        &mut self.clocks
571    }
572
573    /// Returns access to the underlying [`WasiFilesystemCtx`].
574    pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {
575        &mut self.filesystem
576    }
577
578    /// Returns access to the underlying [`WasiCliCtx`].
579    pub fn cli(&mut self) -> &mut WasiCliCtx {
580        &mut self.cli
581    }
582
583    /// Returns access to the underlying [`WasiSocketsCtx`].
584    pub fn sockets(&mut self) -> &mut WasiSocketsCtx {
585        &mut self.sockets
586    }
587}