Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi/src/ctx.rs
3073 views
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.
65
pub fn new() -> Self {
66
Self::default()
67
}
68
69
/// Provides a custom implementation of stdin to use.
70
///
71
/// By default stdin is closed but an example of using the host's native
72
/// stdin looks like:
73
///
74
/// ```
75
/// use wasmtime_wasi::WasiCtx;
76
/// use wasmtime_wasi::cli::stdin;
77
///
78
/// let mut wasi = WasiCtx::builder();
79
/// wasi.stdin(stdin());
80
/// ```
81
///
82
/// Note that inheriting the process's stdin can also be done through
83
/// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin).
84
pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self {
85
self.cli.stdin = Box::new(stdin);
86
self
87
}
88
89
/// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout.
90
pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self {
91
self.cli.stdout = Box::new(stdout);
92
self
93
}
94
95
/// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr.
96
pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self {
97
self.cli.stderr = Box::new(stderr);
98
self
99
}
100
101
/// Configures this context's stdin stream to read the host process's
102
/// stdin.
103
///
104
/// Note that concurrent reads of stdin can produce surprising results so
105
/// when using this it's typically best to have a single wasm instance in
106
/// the process using this.
107
pub fn inherit_stdin(&mut self) -> &mut Self {
108
self.stdin(stdin())
109
}
110
111
/// Configures this context's stdout stream to write to the host process's
112
/// stdout.
113
///
114
/// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)
115
/// multiple instances printing to stdout works well.
116
pub fn inherit_stdout(&mut self) -> &mut Self {
117
self.stdout(stdout())
118
}
119
120
/// Configures this context's stderr stream to write to the host process's
121
/// stderr.
122
///
123
/// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)
124
/// multiple instances printing to stderr works well.
125
pub fn inherit_stderr(&mut self) -> &mut Self {
126
self.stderr(stderr())
127
}
128
129
/// Configures all of stdin, stdout, and stderr to be inherited from the
130
/// host process.
131
///
132
/// See [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) for some rationale
133
/// on why this should only be done in situations of
134
/// one-instance-per-process.
135
pub fn inherit_stdio(&mut self) -> &mut Self {
136
self.inherit_stdin().inherit_stdout().inherit_stderr()
137
}
138
139
/// Configures whether or not blocking operations made through this
140
/// `WasiCtx` are allowed to block the current thread.
141
///
142
/// WASI is currently implemented on top of the Rust
143
/// [Tokio](https://tokio.rs/) library. While most WASI APIs are
144
/// non-blocking some are instead blocking from the perspective of
145
/// WebAssembly. For example opening a file is a blocking operation with
146
/// respect to WebAssembly but it's implemented as an asynchronous operation
147
/// on the host. This is currently done with Tokio's
148
/// [`spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).
149
///
150
/// When WebAssembly is used in a synchronous context then this asynchronous
151
/// operation is quickly turned back into a synchronous operation with a
152
/// `block_on` in Rust. This switching back-and-forth between a blocking a
153
/// non-blocking context can have overhead, and this option exists to help
154
/// alleviate this overhead.
155
///
156
/// This option indicates that for WASI functions that are blocking from the
157
/// perspective of WebAssembly it's ok to block the native thread as well.
158
/// This means that this back-and-forth between async and sync won't happen
159
/// and instead blocking operations are performed on-thread (such as opening
160
/// a file). This can improve the performance of WASI operations when async
161
/// support is disabled.
162
pub fn allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self {
163
self.filesystem.allow_blocking_current_thread = enable;
164
self
165
}
166
167
/// 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
/// ```
186
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
/// ```
207
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.
219
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.
225
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.
233
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.
240
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
/// ```
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.
329
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.
338
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.
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 `wasi:clocks/wall-clock` to use the `clock` specified.
353
///
354
/// By default the host's wall clock is used.
355
pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self {
356
self.clocks.wall_clock = Box::new(clock);
357
self
358
}
359
360
/// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified.
361
///
362
/// By default the host's monotonic clock is used.
363
pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self {
364
self.clocks.monotonic_clock = Box::new(clock);
365
self
366
}
367
368
/// Allow all network addresses accessible to the host.
369
///
370
/// This method will inherit all network addresses meaning that any address
371
/// can be bound by the guest or connected to by the guest using any
372
/// protocol.
373
///
374
/// See also [`WasiCtxBuilder::socket_addr_check`].
375
pub fn inherit_network(&mut self) -> &mut Self {
376
self.socket_addr_check(|_, _| Box::pin(async { true }))
377
}
378
379
/// A check that will be called for each socket address that is used.
380
///
381
/// Returning `true` will permit socket connections to the `SocketAddr`,
382
/// while returning `false` will reject the connection.
383
pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self
384
where
385
F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
386
+ Send
387
+ Sync
388
+ 'static,
389
{
390
self.sockets.socket_addr_check = SocketAddrCheck::new(check);
391
self
392
}
393
394
/// Allow usage of `wasi:sockets/ip-name-lookup`
395
///
396
/// By default this is disabled.
397
pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {
398
self.sockets.allowed_network_uses.ip_name_lookup = enable;
399
self
400
}
401
402
/// Allow usage of UDP.
403
///
404
/// This is enabled by default, but can be disabled if UDP should be blanket
405
/// disabled.
406
pub fn allow_udp(&mut self, enable: bool) -> &mut Self {
407
self.sockets.allowed_network_uses.udp = enable;
408
self
409
}
410
411
/// Allow usage of TCP
412
///
413
/// This is enabled by default, but can be disabled if TCP should be blanket
414
/// disabled.
415
pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
416
self.sockets.allowed_network_uses.tcp = enable;
417
self
418
}
419
420
/// Uses the configured context so far to construct the final [`WasiCtx`].
421
///
422
/// Note that each `WasiCtxBuilder` can only be used to "build" once, and
423
/// calling this method twice will panic.
424
///
425
/// # Panics
426
///
427
/// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
428
/// used to create only a single [`WasiCtx`]. Repeated usage of this method
429
/// is not allowed and should use a second builder instead.
430
pub fn build(&mut self) -> WasiCtx {
431
assert!(!self.built);
432
433
let Self {
434
cli,
435
clocks,
436
filesystem,
437
random,
438
sockets,
439
built: _,
440
} = mem::replace(self, Self::new());
441
self.built = true;
442
443
WasiCtx {
444
cli,
445
clocks,
446
filesystem,
447
random,
448
sockets,
449
}
450
}
451
/// Builds a WASIp1 context instead of a [`WasiCtx`].
452
///
453
/// This method is the same as [`build`](WasiCtxBuilder::build) but it
454
/// creates a [`WasiP1Ctx`] instead. This is intended for use with the
455
/// [`p1`] module of this crate
456
///
457
/// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx
458
/// [`p1`]: crate::p1
459
///
460
/// # Panics
461
///
462
/// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
463
/// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
464
/// usage of this method is not allowed and should use a second builder
465
/// instead.
466
#[cfg(feature = "p1")]
467
pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {
468
let wasi = self.build();
469
crate::p1::WasiP1Ctx::new(wasi)
470
}
471
}
472
473
/// Per-[`Store`] state which holds state necessary to implement WASI from this
474
/// crate.
475
///
476
/// This structure is created through [`WasiCtxBuilder`] and is stored within
477
/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
478
/// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.
479
///
480
/// Note that this structure itself does not have any accessors, it's here for
481
/// internal use within the `wasmtime-wasi` crate's implementation of
482
/// bindgen-generated traits.
483
///
484
/// [`Store`]: wasmtime::Store
485
///
486
/// # Example
487
///
488
/// ```
489
/// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};
490
///
491
/// struct MyState {
492
/// ctx: WasiCtx,
493
/// table: ResourceTable,
494
/// }
495
///
496
/// impl WasiView for MyState {
497
/// fn ctx(&mut self) -> WasiCtxView<'_> {
498
/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
499
/// }
500
/// }
501
///
502
/// impl MyState {
503
/// fn new() -> MyState {
504
/// let mut wasi = WasiCtxBuilder::new();
505
/// wasi.arg("./foo.wasm");
506
/// wasi.arg("--help");
507
/// wasi.env("FOO", "bar");
508
///
509
/// MyState {
510
/// ctx: wasi.build(),
511
/// table: ResourceTable::new(),
512
/// }
513
/// }
514
/// }
515
/// ```
516
#[derive(Default)]
517
pub struct WasiCtx {
518
pub(crate) cli: WasiCliCtx,
519
pub(crate) clocks: WasiClocksCtx,
520
pub(crate) filesystem: WasiFilesystemCtx,
521
pub(crate) random: WasiRandomCtx,
522
pub(crate) sockets: WasiSocketsCtx,
523
}
524
525
impl WasiCtx {
526
/// Convenience function for calling [`WasiCtxBuilder::new`].
527
pub fn builder() -> WasiCtxBuilder {
528
WasiCtxBuilder::new()
529
}
530
531
/// Returns access to the underlying [`WasiRandomCtx`].
532
pub fn random(&mut self) -> &mut WasiRandomCtx {
533
&mut self.random
534
}
535
536
/// Returns access to the underlying [`WasiClocksCtx`].
537
pub fn clocks(&mut self) -> &mut WasiClocksCtx {
538
&mut self.clocks
539
}
540
541
/// Returns access to the underlying [`WasiFilesystemCtx`].
542
pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {
543
&mut self.filesystem
544
}
545
546
/// Returns access to the underlying [`WasiCliCtx`].
547
pub fn cli(&mut self) -> &mut WasiCliCtx {
548
&mut self.cli
549
}
550
551
/// Returns access to the underlying [`WasiSocketsCtx`].
552
pub fn sockets(&mut self) -> &mut WasiSocketsCtx {
553
&mut self.sockets
554
}
555
}
556
557