xref: /wasmtime-44.0.1/crates/wasi/src/ctx.rs (revision 301dc716)
1 use crate::cli::{StdinStream, StdoutStream, WasiCliCtx};
2 use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx};
3 use crate::filesystem::{Dir, WasiFilesystemCtx};
4 use crate::random::WasiRandomCtx;
5 use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtx};
6 use crate::{DirPerms, FilePerms, OpenMode};
7 use cap_rand::RngCore;
8 use cap_std::ambient_authority;
9 use std::future::Future;
10 use std::mem;
11 use std::net::SocketAddr;
12 use std::path::Path;
13 use std::pin::Pin;
14 use tokio::io::{stderr, stdin, stdout};
15 use 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)]
38 pub struct WasiCtxBuilder {
39     cli: WasiCliCtx,
40     clocks: WasiClocksCtx,
41     filesystem: WasiFilesystemCtx,
42     random: WasiRandomCtx,
43     sockets: WasiSocketsCtx,
44     built: bool,
45 }
46 
47 impl 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.
new() -> Self65     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).
stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self84     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.
stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self90     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.
stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self96     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.
inherit_stdin(&mut self) -> &mut Self107     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.
inherit_stdout(&mut self) -> &mut Self116     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.
inherit_stderr(&mut self) -> &mut Self125     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.
inherit_stdio(&mut self) -> &mut Self135     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.
allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self162     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     /// ```
envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self186     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     /// ```
env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self207     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.
inherit_env(&mut self) -> &mut Self219     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.
args(&mut self, args: &[impl AsRef<str>]) -> &mut Self225     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.
arg(&mut self, arg: impl AsRef<str>) -> &mut Self233     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.
inherit_args(&mut self) -> &mut Self240     pub fn inherit_args(&mut self) -> &mut Self {
241         self.cli.arguments.extend(std::env::args());
242         self
243     }
244 
245     /// Configures a "preopened directory" to be available to WebAssembly.
246     ///
247     /// By default WebAssembly does not have access to the filesystem because
248     /// there are no preopened directories. All filesystem operations, such as
249     /// opening a file, are done through a preexisting handle. This means that
250     /// to provide WebAssembly access to a directory it must be configured
251     /// through this API.
252     ///
253     /// WASI will also prevent access outside of files provided here. For
254     /// example `..` can't be used to traverse up from the `host_path` provided here
255     /// to the containing directory.
256     ///
257     /// * `host_path` - a path to a directory on the host to open and make
258     ///   accessible to WebAssembly. Note that the name of this directory in the
259     ///   guest is configured with `guest_path` below.
260     /// * `guest_path` - the name of the preopened directory from WebAssembly's
261     ///   perspective. Note that this does not need to match the host's name for
262     ///   the directory.
263     /// * `dir_perms` - this is the permissions that wasm will have to operate on
264     ///   `guest_path`. This can be used, for example, to provide readonly access to a
265     ///   directory.
266     /// * `file_perms` - similar to `dir_perms` but corresponds to the maximum set
267     ///   of permissions that can be used for any file in this directory.
268     ///
269     /// # Errors
270     ///
271     /// This method will return an error if `host_path` cannot be opened.
272     ///
273     /// # Examples
274     ///
275     /// ```
276     /// use wasmtime_wasi::WasiCtxBuilder;
277     /// use wasmtime_wasi::{DirPerms, FilePerms};
278     ///
279     /// # fn main() {}
280     /// # fn foo() -> wasmtime::Result<()> {
281     /// let mut wasi = WasiCtxBuilder::new();
282     ///
283     /// // Make `./host-directory` available in the guest as `.`
284     /// wasi.preopened_dir("./host-directory", ".", DirPerms::all(), FilePerms::all());
285     ///
286     /// // Make `./readonly` available in the guest as `./ro`
287     /// wasi.preopened_dir("./readonly", "./ro", DirPerms::READ, FilePerms::READ);
288     /// # Ok(())
289     /// # }
290     /// ```
preopened_dir( &mut self, host_path: impl AsRef<Path>, guest_path: impl AsRef<str>, dir_perms: DirPerms, file_perms: FilePerms, ) -> Result<&mut Self>291     pub fn preopened_dir(
292         &mut self,
293         host_path: impl AsRef<Path>,
294         guest_path: impl AsRef<str>,
295         dir_perms: DirPerms,
296         file_perms: FilePerms,
297     ) -> Result<&mut Self> {
298         let dir = cap_std::fs::Dir::open_ambient_dir(host_path.as_ref(), ambient_authority())?;
299         let mut open_mode = OpenMode::empty();
300         if dir_perms.contains(DirPerms::READ) {
301             open_mode |= OpenMode::READ;
302         }
303         if dir_perms.contains(DirPerms::MUTATE) {
304             open_mode |= OpenMode::WRITE;
305         }
306         self.filesystem.preopens.push((
307             Dir::new(
308                 dir,
309                 dir_perms,
310                 file_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.
secure_random(&mut self, random: impl RngCore + Send + 'static) -> &mut Self329     pub fn secure_random(&mut self, random: impl RngCore + 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.
insecure_random(&mut self, insecure_random: impl RngCore + Send + 'static) -> &mut Self338     pub fn insecure_random(&mut self, insecure_random: impl RngCore + 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.
insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self347     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.
max_random_size(&mut self, max_size: u64) -> &mut Self361     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.
wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self369     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.
monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self377     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`].
inherit_network(&mut self) -> &mut Self389     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.
socket_addr_check<F>(&mut self, check: F) -> &mut Self where F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>> + Send + Sync + 'static,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.
allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self411     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     /// This is enabled by default, but can be disabled if UDP should be blanket
419     /// disabled.
allow_udp(&mut self, enable: bool) -> &mut Self420     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     /// This is enabled by default, but can be disabled if TCP should be blanket
428     /// disabled.
allow_tcp(&mut self, enable: bool) -> &mut Self429     pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
430         self.sockets.allowed_network_uses.tcp = enable;
431         self
432     }
433 
434     /// Uses the configured context so far to construct the final [`WasiCtx`].
435     ///
436     /// Note that each `WasiCtxBuilder` can only be used to "build" once, and
437     /// calling this method twice will panic.
438     ///
439     /// # Panics
440     ///
441     /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
442     /// used to create only a single [`WasiCtx`]. Repeated usage of this method
443     /// is not allowed and should use a second builder instead.
build(&mut self) -> WasiCtx444     pub fn build(&mut self) -> WasiCtx {
445         assert!(!self.built);
446 
447         let Self {
448             cli,
449             clocks,
450             filesystem,
451             random,
452             sockets,
453             built: _,
454         } = mem::replace(self, Self::new());
455         self.built = true;
456 
457         WasiCtx {
458             cli,
459             clocks,
460             filesystem,
461             random,
462             sockets,
463         }
464     }
465     /// Builds a WASIp1 context instead of a [`WasiCtx`].
466     ///
467     /// This method is the same as [`build`](WasiCtxBuilder::build) but it
468     /// creates a [`WasiP1Ctx`] instead. This is intended for use with the
469     /// [`p1`] module of this crate
470     ///
471     /// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx
472     /// [`p1`]: crate::p1
473     ///
474     /// # Panics
475     ///
476     /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
477     /// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
478     /// usage of this method is not allowed and should use a second builder
479     /// instead.
480     #[cfg(feature = "p1")]
build_p1(&mut self) -> crate::p1::WasiP1Ctx481     pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {
482         let wasi = self.build();
483         crate::p1::WasiP1Ctx::new(wasi)
484     }
485 }
486 
487 /// Per-[`Store`] state which holds state necessary to implement WASI from this
488 /// crate.
489 ///
490 /// This structure is created through [`WasiCtxBuilder`] and is stored within
491 /// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
492 /// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.
493 ///
494 /// Note that this structure itself does not have any accessors, it's here for
495 /// internal use within the `wasmtime-wasi` crate's implementation of
496 /// bindgen-generated traits.
497 ///
498 /// [`Store`]: wasmtime::Store
499 ///
500 /// # Example
501 ///
502 /// ```
503 /// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};
504 ///
505 /// struct MyState {
506 ///     ctx: WasiCtx,
507 ///     table: ResourceTable,
508 /// }
509 ///
510 /// impl WasiView for MyState {
511 ///     fn ctx(&mut self) -> WasiCtxView<'_> {
512 ///         WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
513 ///     }
514 /// }
515 ///
516 /// impl MyState {
517 ///     fn new() -> MyState {
518 ///         let mut wasi = WasiCtxBuilder::new();
519 ///         wasi.arg("./foo.wasm");
520 ///         wasi.arg("--help");
521 ///         wasi.env("FOO", "bar");
522 ///
523 ///         MyState {
524 ///             ctx: wasi.build(),
525 ///             table: ResourceTable::new(),
526 ///         }
527 ///     }
528 /// }
529 /// ```
530 #[derive(Default)]
531 pub struct WasiCtx {
532     pub(crate) cli: WasiCliCtx,
533     pub(crate) clocks: WasiClocksCtx,
534     pub(crate) filesystem: WasiFilesystemCtx,
535     pub(crate) random: WasiRandomCtx,
536     pub(crate) sockets: WasiSocketsCtx,
537 }
538 
539 impl WasiCtx {
540     /// Convenience function for calling [`WasiCtxBuilder::new`].
builder() -> WasiCtxBuilder541     pub fn builder() -> WasiCtxBuilder {
542         WasiCtxBuilder::new()
543     }
544 
545     /// Returns access to the underlying [`WasiRandomCtx`].
random(&mut self) -> &mut WasiRandomCtx546     pub fn random(&mut self) -> &mut WasiRandomCtx {
547         &mut self.random
548     }
549 
550     /// Returns access to the underlying [`WasiClocksCtx`].
clocks(&mut self) -> &mut WasiClocksCtx551     pub fn clocks(&mut self) -> &mut WasiClocksCtx {
552         &mut self.clocks
553     }
554 
555     /// Returns access to the underlying [`WasiFilesystemCtx`].
filesystem(&mut self) -> &mut WasiFilesystemCtx556     pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {
557         &mut self.filesystem
558     }
559 
560     /// Returns access to the underlying [`WasiCliCtx`].
cli(&mut self) -> &mut WasiCliCtx561     pub fn cli(&mut self) -> &mut WasiCliCtx {
562         &mut self.cli
563     }
564 
565     /// Returns access to the underlying [`WasiSocketsCtx`].
sockets(&mut self) -> &mut WasiSocketsCtx566     pub fn sockets(&mut self) -> &mut WasiSocketsCtx {
567         &mut self.sockets
568     }
569 }
570