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::path::Path;
12use std::pin::Pin;
13use tokio::io::{stderr, stdin, stdout};
14use wasmtime::Result;
15
16/// Builder-style structure used to create a [`WasiCtx`].
17///
18/// This type is used to create a [`WasiCtx`] that is considered per-[`Store`]
19/// state. The [`build`][WasiCtxBuilder::build] method is used to finish the
20/// building process and produce a finalized [`WasiCtx`].
21///
22/// # Examples
23///
24/// ```
25/// use wasmtime_wasi::WasiCtx;
26///
27/// let mut wasi = WasiCtx::builder();
28/// wasi.arg("./foo.wasm");
29/// wasi.arg("--help");
30/// wasi.env("FOO", "bar");
31///
32/// let wasi: WasiCtx = wasi.build();
33/// ```
34///
35/// [`Store`]: wasmtime::Store
36#[derive(Default)]
37pub struct WasiCtxBuilder {
38    cli: WasiCliCtx,
39    clocks: WasiClocksCtx,
40    filesystem: WasiFilesystemCtx,
41    random: WasiRandomCtx,
42    sockets: WasiSocketsCtx,
43    built: bool,
44}
45
46impl WasiCtxBuilder {
47    /// Creates a builder for a new context with default parameters set.
48    ///
49    /// The current defaults are:
50    ///
51    /// * stdin is closed
52    /// * stdout and stderr eat all input and it doesn't go anywhere
53    /// * no env vars
54    /// * no arguments
55    /// * no preopens
56    /// * clocks use the host implementation of wall/monotonic clocks
57    /// * RNGs are all initialized with random state and suitable generator
58    ///   quality to satisfy the requirements of WASI APIs.
59    /// * TCP/UDP are allowed but all addresses are denied by default.
60    /// * `wasi:sockets/ip-name-lookup` is denied by default.
61    ///
62    /// These defaults can all be updated via the various builder configuration
63    /// methods below.
64    pub fn new() -> Self {
65        Self::default()
66    }
67
68    /// Provides a custom implementation of stdin to use.
69    ///
70    /// By default stdin is closed but an example of using the host's native
71    /// stdin looks like:
72    ///
73    /// ```
74    /// use wasmtime_wasi::WasiCtx;
75    /// use wasmtime_wasi::cli::stdin;
76    ///
77    /// let mut wasi = WasiCtx::builder();
78    /// wasi.stdin(stdin());
79    /// ```
80    ///
81    /// Note that inheriting the process's stdin can also be done through
82    /// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin).
83    pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self {
84        self.cli.stdin = Box::new(stdin);
85        self
86    }
87
88    /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout.
89    pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self {
90        self.cli.stdout = Box::new(stdout);
91        self
92    }
93
94    /// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr.
95    pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self {
96        self.cli.stderr = Box::new(stderr);
97        self
98    }
99
100    /// Configures this context's stdin stream to read the host process's
101    /// stdin.
102    ///
103    /// Note that concurrent reads of stdin can produce surprising results so
104    /// when using this it's typically best to have a single wasm instance in
105    /// the process using this.
106    pub fn inherit_stdin(&mut self) -> &mut Self {
107        self.stdin(stdin())
108    }
109
110    /// Configures this context's stdout stream to write to the host process's
111    /// stdout.
112    ///
113    /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)
114    /// multiple instances printing to stdout works well.
115    pub fn inherit_stdout(&mut self) -> &mut Self {
116        self.stdout(stdout())
117    }
118
119    /// Configures this context's stderr stream to write to the host process's
120    /// stderr.
121    ///
122    /// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)
123    /// multiple instances printing to stderr works well.
124    pub fn inherit_stderr(&mut self) -> &mut Self {
125        self.stderr(stderr())
126    }
127
128    /// Configures all of stdin, stdout, and stderr to be inherited from the
129    /// host process.
130    ///
131    /// See [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) for some rationale
132    /// on why this should only be done in situations of
133    /// one-instance-per-process.
134    pub fn inherit_stdio(&mut self) -> &mut Self {
135        self.inherit_stdin().inherit_stdout().inherit_stderr()
136    }
137
138    /// Configures whether or not blocking operations made through this
139    /// `WasiCtx` are allowed to block the current thread.
140    ///
141    /// WASI is currently implemented on top of the Rust
142    /// [Tokio](https://tokio.rs/) library. While most WASI APIs are
143    /// non-blocking some are instead blocking from the perspective of
144    /// WebAssembly. For example opening a file is a blocking operation with
145    /// respect to WebAssembly but it's implemented as an asynchronous operation
146    /// on the host. This is currently done with Tokio's
147    /// [`spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).
148    ///
149    /// When WebAssembly is used in a synchronous context then this asynchronous
150    /// operation is quickly turned back into a synchronous operation with a
151    /// `block_on` in Rust. This switching back-and-forth between a blocking a
152    /// non-blocking context can have overhead, and this option exists to help
153    /// alleviate this overhead.
154    ///
155    /// This option indicates that for WASI functions that are blocking from the
156    /// perspective of WebAssembly it's ok to block the native thread as well.
157    /// This means that this back-and-forth between async and sync won't happen
158    /// and instead blocking operations are performed on-thread (such as opening
159    /// a file). This can improve the performance of WASI operations when async
160    /// support is disabled.
161    pub fn allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self {
162        self.filesystem.allow_blocking_current_thread = enable;
163        self
164    }
165
166    /// Appends multiple environment variables at once for this builder.
167    ///
168    /// All environment variables are appended to the list of environment
169    /// variables that this builder will configure.
170    ///
171    /// At this time environment variables are not deduplicated and if the same
172    /// key is set twice then the guest will see two entries for the same key.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use wasmtime_wasi::WasiCtxBuilder;
178    ///
179    /// let mut wasi = WasiCtxBuilder::new();
180    /// wasi.envs(&[
181    ///     ("FOO", "bar"),
182    ///     ("HOME", "/somewhere"),
183    /// ]);
184    /// ```
185    pub fn envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self {
186        self.cli.environment.extend(
187            env.iter()
188                .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())),
189        );
190        self
191    }
192
193    /// Appends a single environment variable for this builder.
194    ///
195    /// At this time environment variables are not deduplicated and if the same
196    /// key is set twice then the guest will see two entries for the same key.
197    ///
198    /// # Examples
199    ///
200    /// ```
201    /// use wasmtime_wasi::WasiCtxBuilder;
202    ///
203    /// let mut wasi = WasiCtxBuilder::new();
204    /// wasi.env("FOO", "bar");
205    /// ```
206    pub fn env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self {
207        self.cli
208            .environment
209            .push((k.as_ref().to_owned(), v.as_ref().to_owned()));
210        self
211    }
212
213    /// Configures all environment variables to be inherited from the calling
214    /// process into this configuration.
215    ///
216    /// This will use [`envs`](WasiCtxBuilder::envs) to append all host-defined
217    /// environment variables.
218    pub fn inherit_env(&mut self) -> &mut Self {
219        self.cli.environment.extend(std::env::vars());
220        self
221    }
222
223    /// Appends a list of arguments to the argument array to pass to wasm.
224    pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {
225        self.cli
226            .arguments
227            .extend(args.iter().map(|a| a.as_ref().to_owned()));
228        self
229    }
230
231    /// Appends a single argument to get passed to wasm.
232    pub fn arg(&mut self, arg: impl AsRef<str>) -> &mut Self {
233        self.cli.arguments.push(arg.as_ref().to_owned());
234        self
235    }
236
237    /// Appends all host process arguments to the list of arguments to get
238    /// passed to wasm.
239    pub fn inherit_args(&mut self) -> &mut Self {
240        self.cli.arguments.extend(std::env::args());
241        self
242    }
243
244    /// Configures the initial current working directory reported to the guest.
245    ///
246    /// By default no initial current working directory is configured and
247    /// `wasi:cli/environment.initial-cwd` returns `none`.
248    pub fn initial_cwd(&mut self, path: impl AsRef<str>) -> &mut Self {
249        self.cli.initial_cwd = Some(path.as_ref().to_owned());
250        self
251    }
252
253    /// Configures a "preopened directory" to be available to WebAssembly.
254    ///
255    /// By default WebAssembly does not have access to the filesystem because
256    /// there are no preopened directories. All filesystem operations, such as
257    /// opening a file, are done through a preexisting handle. This means that
258    /// to provide WebAssembly access to a directory it must be configured
259    /// through this API.
260    ///
261    /// WASI will also prevent access outside of files provided here. For
262    /// example `..` can't be used to traverse up from the `host_path` provided here
263    /// to the containing directory.
264    ///
265    /// * `host_path` - a path to a directory on the host to open and make
266    ///   accessible to WebAssembly. Note that the name of this directory in the
267    ///   guest is configured with `guest_path` below.
268    /// * `guest_path` - the name of the preopened directory from WebAssembly's
269    ///   perspective. Note that this does not need to match the host's name for
270    ///   the directory.
271    /// * `fs_perms` - permissions enforced by wasmtime-wasi on filesystem
272    ///    operations under a preopen.
273    ///
274    /// # Errors
275    ///
276    /// This method will return an error if `host_path` cannot be opened.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use wasmtime_wasi::WasiCtxBuilder;
282    /// use wasmtime_wasi::FsPerms;
283    ///
284    /// # fn main() {}
285    /// # fn foo() -> wasmtime::Result<()> {
286    /// let mut wasi = WasiCtxBuilder::new();
287    ///
288    /// // Make `./host-directory` available in the guest as `.`
289    /// wasi.preopened_dir("./host-directory", ".", FsPerms::ReadWrite);
290    ///
291    /// // Make `./readonly` available in the guest as `./ro`
292    /// wasi.preopened_dir("./readonly", "./ro", FsPerms::ReadOnly);
293    /// # Ok(())
294    /// # }
295    /// ```
296    pub fn preopened_dir(
297        &mut self,
298        host_path: impl AsRef<Path>,
299        guest_path: impl AsRef<str>,
300        perms: FsPerms,
301    ) -> Result<&mut Self> {
302        let dir = crate::filesystem::primitives::open_ambient_dir(host_path.as_ref())?;
303        let open_mode = match perms {
304            FsPerms::ReadOnly => OpenMode::READ,
305            FsPerms::ReadWrite => OpenMode::READ | OpenMode::WRITE,
306        };
307        self.filesystem.preopens.push((
308            Dir::new(
309                dir,
310                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.
329    pub fn secure_random(&mut self, random: impl Rng + 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.
338    pub fn insecure_random(&mut self, insecure_random: impl Rng + 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.
347    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.
361    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.
369    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.
377    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`].
389    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.
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.
411    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    /// By default this is disabled.
419    pub fn allow_udp(&mut self, enable: bool) -> &mut Self {
420        self.sockets.allowed_network_uses.udp = enable;
421        self
422    }
423
424    /// Allow usage of TCP
425    ///
426    /// By default this is disabled.
427    pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
428        self.sockets.allowed_network_uses.tcp = enable;
429        self
430    }
431
432    /// Uses the configured context so far to construct the final [`WasiCtx`].
433    ///
434    /// Note that each `WasiCtxBuilder` can only be used to "build" once, and
435    /// calling this method twice will panic.
436    ///
437    /// # Panics
438    ///
439    /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
440    /// used to create only a single [`WasiCtx`]. Repeated usage of this method
441    /// is not allowed and should use a second builder instead.
442    pub fn build(&mut self) -> WasiCtx {
443        assert!(!self.built);
444
445        let Self {
446            cli,
447            clocks,
448            filesystem,
449            random,
450            sockets,
451            built: _,
452        } = mem::replace(self, Self::new());
453        self.built = true;
454
455        WasiCtx {
456            cli,
457            clocks,
458            filesystem,
459            random,
460            sockets,
461        }
462    }
463    /// Builds a WASIp1 context instead of a [`WasiCtx`].
464    ///
465    /// This method is the same as [`build`](WasiCtxBuilder::build) but it
466    /// creates a [`WasiP1Ctx`] instead. This is intended for use with the
467    /// [`p1`] module of this crate
468    ///
469    /// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx
470    /// [`p1`]: crate::p1
471    ///
472    /// # Panics
473    ///
474    /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
475    /// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
476    /// usage of this method is not allowed and should use a second builder
477    /// instead.
478    #[cfg(feature = "p1")]
479    pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {
480        let wasi = self.build();
481        crate::p1::WasiP1Ctx::new(wasi)
482    }
483}
484
485/// Per-[`Store`] state which holds state necessary to implement WASI from this
486/// crate.
487///
488/// This structure is created through [`WasiCtxBuilder`] and is stored within
489/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
490/// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.
491///
492/// Note that this structure itself does not have any accessors, it's here for
493/// internal use within the `wasmtime-wasi` crate's implementation of
494/// bindgen-generated traits.
495///
496/// [`Store`]: wasmtime::Store
497///
498/// # Example
499///
500/// ```
501/// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};
502///
503/// struct MyState {
504///     ctx: WasiCtx,
505///     table: ResourceTable,
506/// }
507///
508/// impl WasiView for MyState {
509///     fn ctx(&mut self) -> WasiCtxView<'_> {
510///         WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
511///     }
512/// }
513///
514/// impl MyState {
515///     fn new() -> MyState {
516///         let mut wasi = WasiCtxBuilder::new();
517///         wasi.arg("./foo.wasm");
518///         wasi.arg("--help");
519///         wasi.env("FOO", "bar");
520///
521///         MyState {
522///             ctx: wasi.build(),
523///             table: ResourceTable::new(),
524///         }
525///     }
526/// }
527/// ```
528#[derive(Default)]
529pub struct WasiCtx {
530    pub(crate) cli: WasiCliCtx,
531    pub(crate) clocks: WasiClocksCtx,
532    pub(crate) filesystem: WasiFilesystemCtx,
533    pub(crate) random: WasiRandomCtx,
534    pub(crate) sockets: WasiSocketsCtx,
535}
536
537impl WasiCtx {
538    /// Convenience function for calling [`WasiCtxBuilder::new`].
539    pub fn builder() -> WasiCtxBuilder {
540        WasiCtxBuilder::new()
541    }
542
543    /// Returns access to the underlying [`WasiRandomCtx`].
544    pub fn random(&mut self) -> &mut WasiRandomCtx {
545        &mut self.random
546    }
547
548    /// Returns access to the underlying [`WasiClocksCtx`].
549    pub fn clocks(&mut self) -> &mut WasiClocksCtx {
550        &mut self.clocks
551    }
552
553    /// Returns access to the underlying [`WasiFilesystemCtx`].
554    pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {
555        &mut self.filesystem
556    }
557
558    /// Returns access to the underlying [`WasiCliCtx`].
559    pub fn cli(&mut self) -> &mut WasiCliCtx {
560        &mut self.cli
561    }
562
563    /// Returns access to the underlying [`WasiSocketsCtx`].
564    pub fn sockets(&mut self) -> &mut WasiSocketsCtx {
565        &mut self.sockets
566    }
567}