wasmtime_wasi/
ctx.rs

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