1b2fefe77SDan Gohman# WASI tutorial 2e41d3338SJakub KonkaWe'll split the tutorial into two parts: in the first part we'll walk through 31c0efd03SAlan Fostercompiling C and Rust programs to WASI and executing the compiled WebAssembly module 41c0efd03SAlan Fosterusing `wasmtime` runtime. In the second part we will discuss the compilation of a 51c0efd03SAlan Fostersimpler WebAssembly program written using the WebAssembly text format, and executing 61c0efd03SAlan Fosterthis using the `wasmtime` runtime. 7b2fefe77SDan Gohman 8e41d3338SJakub Konka- [WASI tutorial](#wasi-tutorial) 91c0efd03SAlan Foster - [Running common languages with WASI](#running-common-languages-with-wasi) 10e41d3338SJakub Konka - [Compiling to WASI](#compiling-to-wasi) 11e41d3338SJakub Konka - [From C](#from-c) 12e41d3338SJakub Konka - [From Rust](#from-rust) 1372004aadSNick Fitzgerald - [Executing in Wasmtime](#executing-in-wasmtime) 141c0efd03SAlan Foster - [Web assembly text example](#web-assembly-text-example) 15e41d3338SJakub Konka 161c0efd03SAlan Foster## Running common languages with WASI 1772004aadSNick Fitzgerald### Compiling to WASI 181c0efd03SAlan Foster#### From C 19b2fefe77SDan GohmanLet's start with a simple C program which performs a file copy, which will 20b2fefe77SDan Gohmanshow to compile and run programs, as well as perform simple sandbox 21b2fefe77SDan Gohmanconfiguration. The C code here uses standard POSIX APIs, and doesn't have 22b2fefe77SDan Gohmanany knowledge of WASI, WebAssembly, or sandboxing. 23b2fefe77SDan Gohman 24b2fefe77SDan Gohman```c 25b2fefe77SDan Gohman#include <stdio.h> 26b2fefe77SDan Gohman#include <string.h> 27b2fefe77SDan Gohman#include <stdlib.h> 28b2fefe77SDan Gohman#include <unistd.h> 29b2fefe77SDan Gohman#include <fcntl.h> 30b2fefe77SDan Gohman#include <errno.h> 31b2fefe77SDan Gohman 32658b5aabSVan der Auwermeulen Grégoireint main(int argc, char **argv) { 33e41cae7dSDan Gohman ssize_t n, m; 34b2fefe77SDan Gohman char buf[BUFSIZ]; 35b2fefe77SDan Gohman 36b2fefe77SDan Gohman if (argc != 3) { 37b2fefe77SDan Gohman fprintf(stderr, "usage: %s <from> <to>\n", argv[0]); 38b2fefe77SDan Gohman exit(1); 39b2fefe77SDan Gohman } 40b2fefe77SDan Gohman 41b2fefe77SDan Gohman int in = open(argv[1], O_RDONLY); 42b2fefe77SDan Gohman if (in < 0) { 43b2fefe77SDan Gohman fprintf(stderr, "error opening input %s: %s\n", argv[1], strerror(errno)); 44b2fefe77SDan Gohman exit(1); 45b2fefe77SDan Gohman } 46b2fefe77SDan Gohman 47b2fefe77SDan Gohman int out = open(argv[2], O_WRONLY | O_CREAT, 0660); 48b2fefe77SDan Gohman if (out < 0) { 49b2fefe77SDan Gohman fprintf(stderr, "error opening output %s: %s\n", argv[2], strerror(errno)); 50b2fefe77SDan Gohman exit(1); 51b2fefe77SDan Gohman } 52b2fefe77SDan Gohman 53b2fefe77SDan Gohman while ((n = read(in, buf, BUFSIZ)) > 0) { 54e41cae7dSDan Gohman char *ptr = buf; 55b2fefe77SDan Gohman while (n > 0) { 56e41cae7dSDan Gohman m = write(out, ptr, (size_t)n); 57b2fefe77SDan Gohman if (m < 0) { 58b2fefe77SDan Gohman fprintf(stderr, "write error: %s\n", strerror(errno)); 59b2fefe77SDan Gohman exit(1); 60b2fefe77SDan Gohman } 61b2fefe77SDan Gohman n -= m; 62e41cae7dSDan Gohman ptr += m; 63b2fefe77SDan Gohman } 64b2fefe77SDan Gohman } 65b2fefe77SDan Gohman 66b2fefe77SDan Gohman if (n < 0) { 67b2fefe77SDan Gohman fprintf(stderr, "read error: %s\n", strerror(errno)); 68b2fefe77SDan Gohman exit(1); 69b2fefe77SDan Gohman } 70b2fefe77SDan Gohman 71b2fefe77SDan Gohman return EXIT_SUCCESS; 72b2fefe77SDan Gohman} 73b2fefe77SDan Gohman``` 74b2fefe77SDan Gohman 75b2fefe77SDan GohmanWe'll put this source in a file called `demo.c`. 76b2fefe77SDan Gohman 77fbe29da5SDan GohmanThe [wasi-sdk](https://github.com/WebAssembly/wasi-sdk/releases) provides a clang 783425553dSVan der Auwermeulen Grégoirewhich is configured to target WASI and use the WASI sysroot by default if you put the extracted tree into `/`, so we can 79b2fefe77SDan Gohmancompile our program like so: 80b2fefe77SDan Gohman 81b2fefe77SDan Gohman``` 823425553dSVan der Auwermeulen Grégoire$ clang demo.c -o demo.wasm 833425553dSVan der Auwermeulen Grégoire``` 843425553dSVan der Auwermeulen Grégoire 85d900a5f6SVan der Auwermeulen GrégoireIf you would want to extract it elsewhere, you can specify the sysroot directory like so 863425553dSVan der Auwermeulen Grégoire 873425553dSVan der Auwermeulen Grégoire``` 8815b85dc2SVan der Auwermeulen Grégoire$ clang demo.c --sysroot <path to sysroot> -o demo.wasm 89b2fefe77SDan Gohman``` 90b2fefe77SDan Gohman 91d900a5f6SVan der Auwermeulen GrégoireIf you're using the wasi-sdk, the sysroot directory is located in `opt/wasi-sdk/share/sysroot/` on Linux and mac. 9215b85dc2SVan der Auwermeulen Grégoire 93d900a5f6SVan der Auwermeulen GrégoireThis is just regular clang, configured to use 94d900a5f6SVan der Auwermeulen Grégoirea WebAssembly target and sysroot. The output name specified with the "-o" 95e41d3338SJakub Konkaflag can be anything you want, and *does not* need to contain the `.wasm` extension. 96e41d3338SJakub KonkaIn fact, the output of clang here is a standard WebAssembly module: 97b2fefe77SDan Gohman 98b2fefe77SDan Gohman``` 99e41d3338SJakub Konka$ file demo.wasm 100e41d3338SJakub Konkademo.wasm: WebAssembly (wasm) binary module version 0x1 (MVP) 101b2fefe77SDan Gohman``` 102b2fefe77SDan Gohman 103e41d3338SJakub Konka 1041c0efd03SAlan Foster#### From Rust 105e41d3338SJakub KonkaThe same effect can be achieved with Rust. Firstly, go ahead and create a new 106e41d3338SJakub Konkabinary crate: 107e41d3338SJakub Konka 108e41d3338SJakub Konka``` 109a3e08ee5STshepang Lekhonkhobe$ cargo new demo 110e41d3338SJakub Konka``` 111e41d3338SJakub Konka 112e41d3338SJakub KonkaYou can also clone the Rust code with the crate preset for you from 113e41d3338SJakub Konka[here](https://github.com/kubkon/rust-wasi-tutorial). 114e41d3338SJakub Konka 115e41d3338SJakub KonkaNow, let's port the C program defined in [From C](#from-c) section to Rust: 116e41d3338SJakub Konka 117e41d3338SJakub Konka```rust 118e41d3338SJakub Konkause std::env; 119e41d3338SJakub Konkause std::fs; 120e41d3338SJakub Konkause std::io::{Read, Write}; 121e41d3338SJakub Konka 122e41d3338SJakub Konkafn process(input_fname: &str, output_fname: &str) -> Result<(), String> { 123e41d3338SJakub Konka let mut input_file = 1244c7e66e5Shayasshi fs::File::open(input_fname).map_err(|err| format!("error opening input {}: {}", input_fname, err))?; 125e41d3338SJakub Konka let mut contents = Vec::new(); 126e41d3338SJakub Konka input_file 127e41d3338SJakub Konka .read_to_end(&mut contents) 128e41d3338SJakub Konka .map_err(|err| format!("read error: {}", err))?; 129e41d3338SJakub Konka 130e41d3338SJakub Konka let mut output_file = fs::File::create(output_fname) 1314c7e66e5Shayasshi .map_err(|err| format!("error opening output {}: {}", output_fname, err))?; 132e41d3338SJakub Konka output_file 133e41d3338SJakub Konka .write_all(&contents) 134e41d3338SJakub Konka .map_err(|err| format!("write error: {}", err)) 135e41d3338SJakub Konka} 136e41d3338SJakub Konka 137e41d3338SJakub Konkafn main() { 138e41d3338SJakub Konka let args: Vec<String> = env::args().collect(); 139e41d3338SJakub Konka let program = args[0].clone(); 140e41d3338SJakub Konka 141e41d3338SJakub Konka if args.len() < 3 { 142ebbe3997SPat Hickey eprintln!("usage: {} <from> <to>", program); 143e41d3338SJakub Konka return; 144e41d3338SJakub Konka } 145e41d3338SJakub Konka 146e41d3338SJakub Konka if let Err(err) = process(&args[1], &args[2]) { 147e41d3338SJakub Konka eprintln!("{}", err) 148e41d3338SJakub Konka } 149e41d3338SJakub Konka} 150e41d3338SJakub Konka``` 151e41d3338SJakub Konka 152e41d3338SJakub KonkaLet's put this source in the main file of our crate `src/main.rs`. 153e41d3338SJakub Konka 154e41d3338SJakub KonkaIn order to build it, we first need to install a WASI-enabled Rust toolchain: 155e41d3338SJakub Konka 156e41d3338SJakub Konka``` 15705095c18SAlex Crichton$ rustup target add wasm32-wasip1 15805095c18SAlex Crichton$ cargo build --target wasm32-wasip1 159e41d3338SJakub Konka``` 160e41d3338SJakub Konka 16105095c18SAlex CrichtonWe should now have the WebAssembly module created in `target/wasm32-wasip1/debug`: 162e41d3338SJakub Konka 163e41d3338SJakub Konka``` 16405095c18SAlex Crichton$ file target/wasm32-wasip1/debug/demo.wasm 165e41d3338SJakub Konkademo.wasm: WebAssembly (wasm) binary module version 0x1 (MVP) 166e41d3338SJakub Konka``` 167e41d3338SJakub Konka 16872004aadSNick Fitzgerald### Executing in Wasmtime 169e41d3338SJakub KonkaThe resultant WebAssembly module `demo.wasm` compiled either from C or Rust is simply 170e41d3338SJakub Konkaa single file containing a self-contained wasm module, that doesn't require 171b2fefe77SDan Gohmanany supporting JS code. 172b2fefe77SDan Gohman 173e41d3338SJakub KonkaWe can execute it with `wasmtime` directly, like so: 174b2fefe77SDan Gohman 175b2fefe77SDan Gohman``` 176e41d3338SJakub Konka$ wasmtime demo.wasm 177e41d3338SJakub Konkausage: demo.wasm <from> <to> 178b2fefe77SDan Gohman``` 179b2fefe77SDan Gohman 180b2fefe77SDan GohmanOk, this program needs some command-line arguments. So let's give it some: 181b2fefe77SDan Gohman 182b2fefe77SDan Gohman``` 183b2fefe77SDan Gohman$ echo hello world > test.txt 184e41d3338SJakub Konka$ wasmtime demo.wasm test.txt /tmp/somewhere.txt 18579f33451SJia Tanerror opening input test.txt: No such file or directory 186b2fefe77SDan Gohman``` 187b2fefe77SDan Gohman 188b2fefe77SDan GohmanAha, now we're seeing the sandboxing in action. This program is attempting to 189b2fefe77SDan Gohmanaccess a file by the name of `test.txt`, however it hasn't been given the 190b2fefe77SDan Gohmancapability to do so. 191b2fefe77SDan Gohman 192b2fefe77SDan GohmanSo let's give it capabilities to access files in the requisite directories: 193b2fefe77SDan Gohman 194b2fefe77SDan Gohman``` 195e41d3338SJakub Konka$ wasmtime --dir=. --dir=/tmp demo.wasm test.txt /tmp/somewhere.txt 196b2fefe77SDan Gohman$ cat /tmp/somewhere.txt 197b2fefe77SDan Gohmanhello world 198b2fefe77SDan Gohman``` 199b2fefe77SDan Gohman 200b2fefe77SDan GohmanNow our program runs as expected! 201b2fefe77SDan Gohman 202e41d3338SJakub KonkaWhat's going on under the covers? The `--dir=` option instructs `wasmtime` 203d8242bc6SDan Gohmanto *preopen* a directory, and make it available to the program as a capability 204d8242bc6SDan Gohmanwhich can be used to open files inside that directory. Now when the program 205e41d3338SJakub Konkacalls the C/Rust `open` function, passing it either an absolute or relative path, 206d8242bc6SDan Gohmanthe WASI libc transparently translates that path into a path that's relative to 207d8242bc6SDan Gohmanone of the given preopened directories, if possible (using a technique based 208002a61c3SDan Gohmanon [libpreopen](https://github.com/musec/libpreopen)). This way, we can have a 209d8242bc6SDan Gohmansimple capability-oriented model at the system call level, while portable 210d8242bc6SDan Gohmanapplication code doesn't have to do anything special. 211d8242bc6SDan Gohman 212b2fefe77SDan GohmanAs a brief aside, note that we used the path `.` above to grant the program 213b2fefe77SDan Gohmanaccess to the current directory. This is needed because the mapping from 214b2fefe77SDan Gohmanpaths to associated capabilities is performed by libc, so it's part of the 215b2fefe77SDan GohmanWebAssembly program, and we don't expose the actual current working 216b2fefe77SDan Gohmandirectory to the WebAssembly program. So providing a full path doesn't work: 217b2fefe77SDan Gohman 218b2fefe77SDan Gohman``` 219e41d3338SJakub Konka$ wasmtime --dir=$PWD --dir=/tmp demo.wasm test.txt /tmp/somewhere.txt 22079f33451SJia Tanerror opening input test.txt: No such file or directory 221b2fefe77SDan Gohman``` 222b2fefe77SDan Gohman 223b2fefe77SDan GohmanSo, we always have to use `.` to refer to the current directory. 224b2fefe77SDan Gohman 225b2fefe77SDan GohmanSpeaking of `.`, what about `..`? Does that give programs a way to break 226b2fefe77SDan Gohmanout of the sandbox? Let's see: 227b2fefe77SDan Gohman 228b2fefe77SDan Gohman``` 229e41d3338SJakub Konka$ wasmtime --dir=. --dir=/tmp demo.wasm test.txt /tmp/../etc/passwd 23079f33451SJia Tanerror opening output /tmp/../etc/passwd: Operation not permitted 231b2fefe77SDan Gohman``` 232b2fefe77SDan Gohman 233b2fefe77SDan GohmanThe sandbox says no. And note that this is the capabilities system saying no 23479f33451SJia Tanhere ("Operation not permitted"), rather than Unix access controls 235e41d3338SJakub Konka("Permission denied"). Even if the user running `wasmtime` had write access to 236b2fefe77SDan Gohman`/etc/passwd`, WASI programs don't have the capability to access files outside 237b2fefe77SDan Gohmanof the directories they've been granted. This is true when resolving symbolic 238b2fefe77SDan Gohmanlinks as well. 239b2fefe77SDan Gohman 2408995750aSAlex Crichton`wasmtime` also has the ability to remap directories: 241b2fefe77SDan Gohman 242b2fefe77SDan Gohman``` 243183cb0f2SAlex Crichton$ wasmtime --dir=. --dir=/var/tmp::/tmp demo.wasm test.txt /tmp/somewhere.txt 244b2fefe77SDan Gohman$ cat /var/tmp/somewhere.txt 245b2fefe77SDan Gohmanhello world 246b2fefe77SDan Gohman``` 247b2fefe77SDan Gohman 248b2fefe77SDan GohmanThis maps the name `/tmp` within the WebAssembly program to `/var/tmp` in the 249b2fefe77SDan Gohmanhost filesystem. So the WebAssembly program itself never sees the `/var/tmp` path, 250b2fefe77SDan Gohmanbut that's where the output file goes. 251b2fefe77SDan Gohman 252b2fefe77SDan GohmanSee [here](WASI-capabilities.md) for more information on the capability-based 253b2fefe77SDan Gohmansecurity model. 254b2fefe77SDan Gohman 255b2fefe77SDan GohmanThe capability model is very powerful, and what's shown here is just the beginning. 256b2fefe77SDan GohmanIn the future, we'll be exposing much more functionality, including finer-grained 257b2fefe77SDan Gohmancapabilities, capabilities for network ports, and the ability for applications to 258b2fefe77SDan Gohmanexplicitly request capabilities. 2591c0efd03SAlan Foster 2601c0efd03SAlan Foster## Web assembly text example 2611c0efd03SAlan Foster 2621c0efd03SAlan FosterIn this example we will look at compiling the WebAssembly text format into wasm, and 2631c0efd03SAlan Fosterrunning the compiled WebAssembly module using the `wasmtime` runtime. This example 2641c0efd03SAlan Fostermakes use of WASI's `fd_write` implementation to write `hello world` to stdout. 2651c0efd03SAlan Foster 2661c0efd03SAlan FosterFirst, create a new `demo.wat` file: 2671c0efd03SAlan Foster 2681c0efd03SAlan Foster```wat 2691c0efd03SAlan Foster(module 2701c0efd03SAlan Foster ;; Import the required fd_write WASI function which will write the given io vectors to stdout 2711c0efd03SAlan Foster ;; The function signature for fd_write is: 2729be5dd7cSKevin Gibbons ;; (File Descriptor, *iovs, iovs_len, *nwritten) -> Returns 0 on success, nonzero on error 273af38ee09SMatthew Phillips (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) 2741c0efd03SAlan Foster 2751c0efd03SAlan Foster (memory 1) 2761c0efd03SAlan Foster (export "memory" (memory 0)) 2771c0efd03SAlan Foster 2781c0efd03SAlan Foster ;; Write 'hello world\n' to memory at an offset of 8 bytes 2791c0efd03SAlan Foster ;; Note the trailing newline which is required for the text to appear 2801c0efd03SAlan Foster (data (i32.const 8) "hello world\n") 2811c0efd03SAlan Foster 2821c0efd03SAlan Foster (func $main (export "_start") 2831c0efd03SAlan Foster ;; Creating a new io vector within linear memory 2841c0efd03SAlan Foster (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string 2851c0efd03SAlan Foster (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string 2861c0efd03SAlan Foster 2871c0efd03SAlan Foster (call $fd_write 2881c0efd03SAlan Foster (i32.const 1) ;; file_descriptor - 1 for stdout 2891c0efd03SAlan Foster (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 2901c0efd03SAlan Foster (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. 291038a3874SDaniel Salvadori (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written 2921c0efd03SAlan Foster ) 2930e3dcaebSMischa Spiegelmock drop ;; Discard the number of bytes written from the top of the stack 2941c0efd03SAlan Foster ) 2951c0efd03SAlan Foster) 2961c0efd03SAlan Foster``` 2971c0efd03SAlan Foster 2981c0efd03SAlan Foster`wasmtime` can directly execute `.wat` files: 2991c0efd03SAlan Foster 3001c0efd03SAlan Foster``` 3011c0efd03SAlan Foster$ wasmtime demo.wat 3021c0efd03SAlan Fosterhello world 3031c0efd03SAlan Foster``` 3041c0efd03SAlan Foster 3051c0efd03SAlan FosterOr, you can compile the `.wat` WebAssembly text format into the wasm binary format 306*3f2febf0SDan Gohmanyourself using the [wasm-tools] command line tools: 3071c0efd03SAlan Foster 3081c0efd03SAlan Foster``` 309*3f2febf0SDan Gohman$ wasm-tools parse demo.wat -o demo.wasm 3101c0efd03SAlan Foster``` 3111c0efd03SAlan Foster 3121c0efd03SAlan FosterThe created `.wasm` file can now be executed with `wasmtime` directly like so: 3131c0efd03SAlan Foster 3141c0efd03SAlan Foster``` 3151c0efd03SAlan Foster$ wasmtime demo.wasm 3161c0efd03SAlan Fosterhello world 3171c0efd03SAlan Foster``` 3181c0efd03SAlan Foster 319*3f2febf0SDan GohmanTo run this example within the browser, use [jco]. 3201c0efd03SAlan Foster 321*3f2febf0SDan Gohman[wasm-tools]: https://github.com/bytecodealliance/wasm-tools 322*3f2febf0SDan Gohman[jco]: https://github.com/bytecodealliance/jco 323