WebAssembly Runtimes - WASI and Wasmtime
Tags: webassembly
Published: 2019-10-18 02:54:32


After a while (and a [release](https://www.enlightenment.org/news/efl-1.23.0) at work), here we are again. To continue the series, we will take a quick look at wasmtime and WASI.

Wasmtime
--------

[Wasmtime](https://github.com/CraneStation/wasmtime) is a "standalone wasm-only runtime for WebAssembly using the [Cranelift](https://github.com/CraneStation/cranelift) JIT". It is part of CraneStation, an "umbrella" project for compiler tools where Mozilla is one (if not the main) parties involved.

The Cranelift JIT is kinda similar to LLVM. Other runtimes like Wasmer - which we will talk about in a future post can switch between Cranelift and LLVM as the backend to generate the code.

But before we go into more wasmtime details, it is important to talk about how we will be interacting with these runtimes.

WASI
----

As I said in the first post, portability is an important target of WebAssembly. Not only about running a given `.wasm` file in any place, but also about WebAssembly providing a - as much as possible - portable interface to build programs on top of it. This led to the development of the WebAssembly System Interface - WASI ([here is a great post](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/) by the great Lin Clark about what is a system interface and why, what, how and who is developing one for WebAssembly).

While system interfaces like POSIX served us well for decades, the challenges of a truly universal system interface in the context of WASM are more strict. This happens because, like a webpage in the browser, you could be running WASM programs from any source and this demands a new approach to security.

WASI thus is being developed with a capability-oriented approach. Instead of the POSIX way of a program inheriting all access rights of the user, in WASI it behaves kinda like installing an app on your phone. Each time a WASM program tries to access a restricted resource, the runtime checks if the user granted access to that resource. We will see more details later in a WASI example.

To begin, we need to install the `wasi-sdk` that will make it easier for us to create WASI-ready wasm files. It is a clang-based toolchain that is also part of the CraneStation initiative. There are packages [available in Github](https://github.com/CraneStation/wasi-sdk/releases). In my Ubuntu install, the executables were installed to `/opt/wasi-sdk/bin`. The tutorial for wasi-sdk is [here](https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-tutorial.md).

Getting wasmtime
----------------

The easiest way to get wasmtime is by running their installer with the command below. It will install the runtime to `$HOME/.wasmtime/bin` and automatically set up the profile scripts to add it to the `$PATH` when you open a new terminal. Don't forget to re-add the wasi-sdk bin folder to `PATH` in this new terminal.

```
$ curl https://wasmtime.dev/install.sh -sSf | bash
```

As expected a hello world in wasm/wasi is the same as in the desktop:

```c
#include <stdio.h>

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}
```

To compile, you use the `wasi-sdk` `clang` just like you would with a regular C compiler:

```
$ clang hello.c -o hello.wasm
```

And to run, just call the wasmtime executable:

```
$ wasmtime hello.wasm
Hello, World
```

That's it. You should have the message printed in your terminal. WASI allows the standard output and error descriptors (`stdout` and `stderr`) to be freely accessed.

Now, what if we tried to access files? For example, let's implement a `cat` program. It just reads a file and prints it to the terminal. Save the program below to a file named `cat.c`.

```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char* argv[])
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s <input file>\n", argv[0]);
        exit(1);
    }

    int n = 0;
    char buf[BUFSIZ];

    int f = open(argv[1], O_RDONLY);
    if (f < 0) {
        fprintf(stderr, "error opening file %s: %s\n", argv[1], strerror(errno));
        exit(0);
    }

    while((n = read(f, buf, BUFSIZ)) > 0) {
        printf("%.*s", n, buf);
    }

    if (n < 0) {
        fprintf(stderr, "read error: %s\n", strerror(errno));
        exit(0);
    }
    return 0;
}
```

Like the previous example, compile...

```
$ clang cat.c -o cat.wasm
```

And run...

```
$ wasmtime cat.wasm hello.c
error opening file hello.c: Capabilities insufficient
```

Ooops. Looks like reading files is a restricted operation. Which makes sense, given files could hold sensitive information. So, how do we grant access to them? With wasmtime, just use the option `--dir=<path>`:

```
$ wasmtime --dir=. cat.wasm hello.c
#include <stdio.h>...
```

Conclusion
----------

This was just a gentle introduction to using `wasi-sdk` and `wasmtime` to play with WASI-based Webassembly programs. In the next post, we will see how to embed wasmtime into other programs and do more complex interactions.