wasmtime_wasi/p2/ctx.rs
1use crate::clocks::{
2 host::{monotonic_clock, wall_clock},
3 HostMonotonicClock, HostWallClock,
4};
5use crate::net::{SocketAddrCheck, SocketAddrUse};
6use crate::p2::{
7 filesystem::Dir,
8 pipe, stdio,
9 stdio::{StdinStream, StdoutStream},
10};
11use crate::{random, DirPerms, FilePerms, OpenMode};
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::p2::{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::p2::{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::p2::{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::p2::{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::p2::WasiCtxBuilder;
313 /// use wasmtime_wasi::{DirPerms, FilePerms};
314 ///
315 /// # fn main() {}
316 /// # fn foo() -> wasmtime::Result<()> {
317 /// let mut wasi = WasiCtxBuilder::new();
318 ///
319 /// // Make `./host-directory` available in the guest as `.`
320 /// wasi.preopened_dir("./host-directory", ".", DirPerms::all(), FilePerms::all());
321 ///
322 /// // Make `./readonly` available in the guest as `./ro`
323 /// wasi.preopened_dir("./readonly", "./ro", DirPerms::READ, FilePerms::READ);
324 /// # Ok(())
325 /// # }
326 /// ```
327 pub fn preopened_dir(
328 &mut self,
329 host_path: impl AsRef<Path>,
330 guest_path: impl AsRef<str>,
331 dir_perms: DirPerms,
332 file_perms: FilePerms,
333 ) -> Result<&mut Self> {
334 let dir = cap_std::fs::Dir::open_ambient_dir(host_path.as_ref(), ambient_authority())?;
335 let mut open_mode = OpenMode::empty();
336 if dir_perms.contains(DirPerms::READ) {
337 open_mode |= OpenMode::READ;
338 }
339 if dir_perms.contains(DirPerms::MUTATE) {
340 open_mode |= OpenMode::WRITE;
341 }
342 self.preopens.push((
343 Dir::new(
344 dir,
345 dir_perms,
346 file_perms,
347 open_mode,
348 self.allow_blocking_current_thread,
349 ),
350 guest_path.as_ref().to_owned(),
351 ));
352 Ok(self)
353 }
354
355 /// Set the generator for the `wasi:random/random` number generator to the
356 /// custom generator specified.
357 ///
358 /// Note that contexts have a default RNG configured which is a suitable
359 /// generator for WASI and is configured with a random seed per-context.
360 ///
361 /// Guest code may rely on this random number generator to produce fresh
362 /// unpredictable random data in order to maintain its security invariants,
363 /// and ideally should use the insecure random API otherwise, so using any
364 /// prerecorded or otherwise predictable data may compromise security.
365 pub fn secure_random(&mut self, random: impl RngCore + Send + 'static) -> &mut Self {
366 self.random = Box::new(random);
367 self
368 }
369
370 /// Configures the generator for `wasi:random/insecure`.
371 ///
372 /// The `insecure_random` generator provided will be used for all randomness
373 /// requested by the `wasi:random/insecure` interface.
374 pub fn insecure_random(&mut self, insecure_random: impl RngCore + Send + 'static) -> &mut Self {
375 self.insecure_random = Box::new(insecure_random);
376 self
377 }
378
379 /// Configures the seed to be returned from `wasi:random/insecure-seed` to
380 /// the specified custom value.
381 ///
382 /// By default this number is randomly generated when a builder is created.
383 pub fn insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self {
384 self.insecure_random_seed = insecure_random_seed;
385 self
386 }
387
388 /// Configures `wasi:clocks/wall-clock` to use the `clock` specified.
389 ///
390 /// By default the host's wall clock is used.
391 pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self {
392 self.wall_clock = Box::new(clock);
393 self
394 }
395
396 /// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified.
397 ///
398 /// By default the host's monotonic clock is used.
399 pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self {
400 self.monotonic_clock = Box::new(clock);
401 self
402 }
403
404 /// Allow all network addresses accessible to the host.
405 ///
406 /// This method will inherit all network addresses meaning that any address
407 /// can be bound by the guest or connected to by the guest using any
408 /// protocol.
409 ///
410 /// See also [`WasiCtxBuilder::socket_addr_check`].
411 pub fn inherit_network(&mut self) -> &mut Self {
412 self.socket_addr_check(|_, _| Box::pin(async { true }))
413 }
414
415 /// A check that will be called for each socket address that is used.
416 ///
417 /// Returning `true` will permit socket connections to the `SocketAddr`,
418 /// while returning `false` will reject the connection.
419 pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self
420 where
421 F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
422 + Send
423 + Sync
424 + 'static,
425 {
426 self.socket_addr_check = SocketAddrCheck(Arc::new(check));
427 self
428 }
429
430 /// Allow usage of `wasi:sockets/ip-name-lookup`
431 ///
432 /// By default this is disabled.
433 pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {
434 self.allowed_network_uses.ip_name_lookup = enable;
435 self
436 }
437
438 /// Allow usage of UDP.
439 ///
440 /// This is enabled by default, but can be disabled if UDP should be blanket
441 /// disabled.
442 pub fn allow_udp(&mut self, enable: bool) -> &mut Self {
443 self.allowed_network_uses.udp = enable;
444 self
445 }
446
447 /// Allow usage of TCP
448 ///
449 /// This is enabled by default, but can be disabled if TCP should be blanket
450 /// disabled.
451 pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
452 self.allowed_network_uses.tcp = enable;
453 self
454 }
455
456 /// Uses the configured context so far to construct the final [`WasiCtx`].
457 ///
458 /// Note that each `WasiCtxBuilder` can only be used to "build" once, and
459 /// calling this method twice will panic.
460 ///
461 /// # Panics
462 ///
463 /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
464 /// used to create only a single [`WasiCtx`]. Repeated usage of this method
465 /// is not allowed and should use a second builder instead.
466 pub fn build(&mut self) -> WasiCtx {
467 assert!(!self.built);
468
469 let Self {
470 stdin,
471 stdout,
472 stderr,
473 env,
474 args,
475 preopens,
476 socket_addr_check,
477 random,
478 insecure_random,
479 insecure_random_seed,
480 wall_clock,
481 monotonic_clock,
482 allowed_network_uses,
483 allow_blocking_current_thread,
484 built: _,
485 } = mem::replace(self, Self::new());
486 self.built = true;
487
488 WasiCtx {
489 stdin,
490 stdout,
491 stderr,
492 env,
493 args,
494 preopens,
495 socket_addr_check,
496 random,
497 insecure_random,
498 insecure_random_seed,
499 wall_clock,
500 monotonic_clock,
501 allowed_network_uses,
502 allow_blocking_current_thread,
503 }
504 }
505
506 /// Builds a WASIp1 context instead of a [`WasiCtx`].
507 ///
508 /// This method is the same as [`build`](WasiCtxBuilder::build) but it
509 /// creates a [`WasiP1Ctx`] instead. This is intended for use with the
510 /// [`preview1`] module of this crate
511 ///
512 /// [`WasiP1Ctx`]: crate::preview1::WasiP1Ctx
513 /// [`preview1`]: crate::preview1
514 ///
515 /// # Panics
516 ///
517 /// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
518 /// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
519 /// usage of this method is not allowed and should use a second builder
520 /// instead.
521 #[cfg(feature = "preview1")]
522 pub fn build_p1(&mut self) -> crate::preview1::WasiP1Ctx {
523 let wasi = self.build();
524 crate::preview1::WasiP1Ctx::new(wasi)
525 }
526}
527
528/// Per-[`Store`] state which holds state necessary to implement WASI from this
529/// crate.
530///
531/// This structure is created through [`WasiCtxBuilder`] and is stored within
532/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
533/// through the [`WasiView`](crate::p2::WasiView) trait as an implementation on `T`.
534///
535/// Note that this structure itself does not have any accessors, it's here for
536/// internal use within the `wasmtime-wasi` crate's implementation of
537/// bindgen-generated traits.
538///
539/// [`Store`]: wasmtime::Store
540///
541/// # Example
542///
543/// ```
544/// use wasmtime_wasi::ResourceTable;
545/// use wasmtime_wasi::p2::{WasiCtx, WasiView, IoView, WasiCtxBuilder};
546///
547/// struct MyState {
548/// ctx: WasiCtx,
549/// table: ResourceTable,
550/// }
551///
552/// impl IoView for MyState {
553/// fn table(&mut self) -> &mut ResourceTable { &mut self.table }
554/// }
555/// impl WasiView for MyState {
556/// fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx }
557/// }
558///
559/// impl MyState {
560/// fn new() -> MyState {
561/// let mut wasi = WasiCtxBuilder::new();
562/// wasi.arg("./foo.wasm");
563/// wasi.arg("--help");
564/// wasi.env("FOO", "bar");
565///
566/// MyState {
567/// ctx: wasi.build(),
568/// table: ResourceTable::new(),
569/// }
570/// }
571/// }
572/// ```
573pub struct WasiCtx {
574 pub(crate) random: Box<dyn RngCore + Send>,
575 pub(crate) insecure_random: Box<dyn RngCore + Send>,
576 pub(crate) insecure_random_seed: u128,
577 pub(crate) wall_clock: Box<dyn HostWallClock + Send>,
578 pub(crate) monotonic_clock: Box<dyn HostMonotonicClock + Send>,
579 pub(crate) env: Vec<(String, String)>,
580 pub(crate) args: Vec<String>,
581 pub(crate) preopens: Vec<(Dir, String)>,
582 pub(crate) stdin: Box<dyn StdinStream>,
583 pub(crate) stdout: Box<dyn StdoutStream>,
584 pub(crate) stderr: Box<dyn StdoutStream>,
585 pub(crate) socket_addr_check: SocketAddrCheck,
586 pub(crate) allowed_network_uses: AllowedNetworkUses,
587 pub(crate) allow_blocking_current_thread: bool,
588}
589
590impl WasiCtx {
591 /// Convenience function for calling [`WasiCtxBuilder::new`].
592 pub fn builder() -> WasiCtxBuilder {
593 WasiCtxBuilder::new()
594 }
595}
596
597pub struct AllowedNetworkUses {
598 pub ip_name_lookup: bool,
599 pub udp: bool,
600 pub tcp: bool,
601}
602
603impl Default for AllowedNetworkUses {
604 fn default() -> Self {
605 Self {
606 ip_name_lookup: false,
607 udp: true,
608 tcp: true,
609 }
610 }
611}
612
613impl AllowedNetworkUses {
614 pub(crate) fn check_allowed_udp(&self) -> std::io::Result<()> {
615 if !self.udp {
616 return Err(std::io::Error::new(
617 std::io::ErrorKind::PermissionDenied,
618 "UDP is not allowed",
619 ));
620 }
621
622 Ok(())
623 }
624
625 pub(crate) fn check_allowed_tcp(&self) -> std::io::Result<()> {
626 if !self.tcp {
627 return Err(std::io::Error::new(
628 std::io::ErrorKind::PermissionDenied,
629 "TCP is not allowed",
630 ));
631 }
632
633 Ok(())
634 }
635}