nodejs / uvwasi

WASI syscall API built atop libuv
MIT License
222 stars 48 forks source link
vewasiraptors

uvwasi

This project does not currently provide the comprehensive file system security properties provided by some WASI runtimes. Full support for secure file system sandboxing may or may not be implemented in future. In the mean time, do not rely on it to run untrusted code.

uvwasi implements the WASI system call API, so that WebAssembly runtimes can easily implement WASI calls. Under the hood, uvwasi leverages libuv where possible for maximum portability.

                              |
WebAssembly code              |      WebAssembly application code
                              |                 |
                              |                 v
                              | WASI syscalls (inserted by compiler toolchain)
                              |                 |
------------------------------+                 |
                              |                 v
WebAssembly runtime (Node.js) |    uvwasi (implementation of WASI)
                              |                 |
                              |                 v
                              |               libuv
                              |                 |
                              |                 v
                              |        platform-specific calls
                              |

(Hence uvwasi isn't for making C-programs-that-use-libuv-APIs execute on WASI runtimes. That would either be a new platform added by libuv, or done through POSIX emulation by the Emscripten or wasi-sdk toolchains.)

Building Locally

To build with CMake:

$ mkdir -p out/cmake ; cd out/cmake   # create build directory
$ cmake ../.. -DBUILD_TESTING=ON      # generate project with test
$ cmake --build .                     # build
$ ctest -C Debug --output-on-failure  # run tests

Example Usage

#include <stdlib.h>
#include <assert.h>
#include "uv.h"
#include "uvwasi.h"

int main(void) {
  uvwasi_t uvwasi;
  uvwasi_options_t init_options;
  uvwasi_errno_t err;

  /* Setup the initialization options. */
  init_options.in = 0;
  init_options.out = 1;
  init_options.err = 2;
  init_options.fd_table_size = 3;
  init_options.argc = 3;
  init_options.argv = calloc(3, sizeof(char*));
  init_options.argv[0] = "--foo=bar";
  init_options.argv[1] = "-baz";
  init_options.argv[2] = "100";
  init_options.envp = NULL;
  init_options.preopenc = 1;
  init_options.preopens = calloc(1, sizeof(uvwasi_preopen_t));
  init_options.preopens[0].mapped_path = "/var";
  init_options.preopens[0].real_path = ".";
  init_options.allocator = NULL;

  /* Initialize the sandbox. */
  err = uvwasi_init(&uvwasi, &init_options);
  assert(err == UVWASI_ESUCCESS);

  /* TODO(cjihrig): Show an example system call or two. */

  /* Clean up resources. */
  uvwasi_destroy(&uvwasi);
  return 0;
}

API

The WASI API is versioned. This documentation is based on the WASI preview 1 snapshot. uvwasi implements the WASI system call API with the following additions/modifications:

Unofficial APIs

This section contains data types and functions for working with uvwasi. They are not part of the official WASI API, but are used to embed uvwasi.

UVWASI_VERSION_MAJOR

The major release version of the uvwasi library. uvwasi follows semantic versioning. Changes to this value represent breaking changes in the public API.

UVWASI_VERSION_MINOR

The minor release version of the uvwasi library. uvwasi follows semantic versioning. Changes to this value represent feature additions in the public API.

UVWASI_VERSION_PATCH

The patch release version of the uvwasi library. uvwasi follows semantic versioning. Changes to this value represent bug fixes in the public API.

UVWASI_VERSION_HEX

The major, minor, and patch versions of the uvwasi library encoded as a single integer value.

UVWASI_VERSION_STRING

The major, minor, and patch versions of the uvwasi library encoded as a version string.

UVWASI_VERSION_WASI

The version of the WASI API targeted by uvwasi.

uvwasi_t

An individual WASI sandbox instance.

typedef struct uvwasi_s {
  struct uvwasi_fd_table_t fds;
  uvwasi_size_t argc;
  char** argv;
  char* argv_buf;
  uvwasi_size_t argv_buf_size;
  uvwasi_size_t envc;
  char** env;
  char* env_buf;
  uvwasi_size_t env_buf_size;
} uvwasi_t;

uvwasi_preopen_t

A data structure used to map a directory path within a WASI sandbox to a directory path on the WASI host platform.

typedef struct uvwasi_preopen_s {
  char* mapped_path;
  char* real_path;
} uvwasi_preopen_t;

uvwasi_options_t

A data structure used to pass configuration options to uvwasi_init().

typedef struct uvwasi_options_s {
  uvwasi_size_t fd_table_size;
  uvwasi_size_t preopenc;
  uvwasi_preopen_t* preopens;
  uvwasi_size_t argc;
  char** argv;
  char** envp;
  uvwasi_fd_t in;
  uvwasi_fd_t out;
  uvwasi_fd_t err;
  const uvwasi_mem_t* allocator;
} uvwasi_options_t;

uvwasi_init()

Initializes a sandbox represented by a uvwasi_t using the options represented by a uvwasi_options_t.

Inputs:

Outputs:

Returns:

uvwasi_destroy()

Cleans up resources related to a WASI sandbox. This function notably does not return an error code.

Inputs:

Outputs:

Returns:

System Calls

This section has been adapted from the official WASI API documentation.

uvwasi_args_get()

Read command-line argument data.

The sizes of the buffers should match that returned by uvwasi_args_sizes_get().

Inputs:

uvwasi_args_sizes_get()

Return command-line argument data sizes.

Outputs:

uvwasi_clock_res_get()

Return the resolution of a clock.

Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, return UVWASI_EINVAL.

Note: This is similar to clock_getres in POSIX.

Inputs:

Outputs:

uvwasi_clock_time_get()

Return the time value of a clock.

Note: This is similar to clock_gettime in POSIX.

Inputs:

Outputs:

uvwasi_environ_get()

Read environment variable data.

The sizes of the buffers should match that returned by uvwasi_environ_sizes_get().

Inputs:

uvwasi_environ_sizes_get()

Return command-line argument data sizes.

Outputs:

uvwasi_fd_advise()

Provide file advisory information on a file descriptor.

Note: This is similar to posix_fadvise in POSIX.

Inputs:

uvwasi_fd_allocate()

Force the allocation of space in a file.

Note: This is similar to posix_fallocate in POSIX.

Inputs:

uvwasi_fd_close()

Close a file descriptor.

Note: This is similar to close in POSIX.

Inputs:

uvwasi_fd_datasync()

Synchronize the data of a file to disk.

Note: This is similar to fdatasync in POSIX.

Inputs:

uvwasi_fd_fdstat_get()

Get the attributes of a file descriptor.

Note: This returns similar flags to fsync(fd, F_GETFL) in POSIX, as well as additional fields.

Inputs:

uvwasi_fd_fdstat_set_flags()

Adjust the flags associated with a file descriptor.

Note: This is similar to fcntl(fd, F_SETFL, flags) in POSIX.

Inputs:

uvwasi_fd_fdstat_set_rights()

Adjust the rights associated with a file descriptor.

This can only be used to remove rights, and returns UVWASI_ENOTCAPABLE if called in a way that would attempt to add rights.

Inputs:

uvwasi_fd_filestat_get()

Return the attributes of an open file.

Inputs:

uvwasi_fd_filestat_set_size()

Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.

Note: This is similar to ftruncate in POSIX.

Inputs:

uvwasi_fd_filestat_set_times()

Adjust the timestamps of an open file or directory.

Note: This is similar to futimens in POSIX.

Inputs:

uvwasi_fd_pread()

Read from a file descriptor, without using and updating the file descriptor's offset.

Note: This is similar to preadv in POSIX.

Inputs:

Outputs:

uvwasi_fd_prestat_get()

Return a description of the given preopened file descriptor.

Inputs:

uvwasi_fd_prestat_dir_name()

Return a description of the given preopened file descriptor.

Inputs:

uvwasi_fd_pwrite()

Write to a file descriptor, without using and updating the file descriptor's offset.

Note: This is similar to pwritev in POSIX.

Inputs:

Outputs:

uvwasi_fd_read()

Read from a file descriptor.

Note: This is similar to readv in POSIX.

Inputs:

Outputs:

uvwasi_fd_readdir()

Read directory entries from a directory.

When successful, the contents of the output buffer consist of a sequence of directory entries. Each directory entry consists of a uvwasi_dirent_t object, followed by uvwasi_dirent_t::d_namlen bytes holding the name of the directory entry.

This function fills the output buffer as much as possible, potentially truncating the last directory entry. This allows the caller to grow its read buffer size in case it's too small to fit a single large directory entry, or skip the oversized directory entry.

Inputs:

Outputs:

uvwasi_fd_renumber()

Atomically replace a file descriptor by renumbering another file descriptor.

Due to the strong focus on thread safety, this environment does not provide a mechanism to duplicate or renumber a file descriptor to an arbitrary number, like dup2(). This would be prone to race conditions, as an actual file descriptor with the same number could be allocated by a different thread at the same time.

This function provides a way to atomically renumber file descriptors, which would disappear if dup2() were to be removed entirely.

Inputs:

uvwasi_fd_seek()

Move the offset of a file descriptor.

Note: This is similar to lseek in POSIX.

Inputs:

Outputs:

uvwasi_fd_sync()

Synchronize the data and metadata of a file to disk.

Note: This is similar to fsync in POSIX.

Inputs:

uvwasi_fd_tell()

Return the current offset of a file descriptor.

Note: This is similar to lseek(fd, 0, SEEK_CUR) in POSIX.

Inputs:

Outputs:

uvwasi_fd_write()

Write to a file descriptor.

Note: This is similar to writev in POSIX.

Inputs:

Outputs:

uvwasi_path_create_directory()

Create a directory.

Note: This is similar to mkdirat in POSIX.

Inputs:

uvwasi_path_filestat_get()

Return the attributes of a file or directory.

Note: This is similar to stat in POSIX.

Inputs:

uvwasi_path_filestat_set_times()

Adjust the timestamps of a file or directory.

Note: This is similar to utimensat in POSIX.

Inputs:

uvwasi_path_link()

Create a hard link.

Note: This is similar to linkat in POSIX.

Inputs:

uvwasi_path_open()

Open a file or directory.

The returned file descriptor is not guaranteed to be the lowest-numbered file descriptor not currently open; it is randomized to prevent applications from depending on making assumptions about indexes, since this is error-prone in multi-threaded contexts. The returned file descriptor is guaranteed to be less than 231.

Note: This is similar to openat in POSIX.

Inputs:

Outputs:

uvwasi_path_readlink()

Read the contents of a symbolic link.

Note: This is similar to readlinkat in POSIX.

Inputs:

Outputs:

uvwasi_path_remove_directory()

Remove a directory.

Return UVWASI_ENOTEMPTY if the directory is not empty.

Note: This is similar to unlinkat(fd, path, AT_REMOVEDIR) in POSIX.

Inputs:

uvwasi_path_rename()

Rename a file or directory.

Note: This is similar to renameat in POSIX.

Inputs:

uvwasi_path_symlink()

Create a symbolic link.

Note: This is similar to symlinkat in POSIX.

Inputs:

uvwasi_path_unlink_file()

Unlink a file.

Return UVWASI_EISDIR if the path refers to a directory.

Note: This is similar to unlinkat(fd, path, 0) in POSIX.

Inputs:

uvwasi_poll_oneoff()

Concurrently poll for the occurrence of a set of events.

Inputs:

Outputs:

uvwasi_proc_exit()

Terminate the process normally. An exit code of 0 indicates successful termination of the program. The meanings of other values is dependent on the environment.

Note: This is similar to _Exit in POSIX.

Inputs:

Does not return.

uvwasi_proc_raise()

Send a signal to the process of the calling thread.

Note: This is similar to raise in POSIX.

Inputs:

uvwasi_random_get()

Write high-quality random data into a buffer.

This function blocks when the implementation is unable to immediately provide sufficient high-quality random data.

This function may execute slowly, so when large mounts of random data are required, it's advisable to use this function to seed a pseudo-random number generator, rather than to provide the random data directly.

Inputs:

uvwasi_sched_yield()

Temporarily yield execution of the calling thread.

Note: This is similar to sched_yield in POSIX.

uvwasi_sock_recv()

Receive a message from a socket.

Note: This is similar to recv in POSIX, though it also supports reading the data into multiple buffers in the manner of readv.

Inputs:

Outputs:

uvwasi_sock_send()

Send a message on a socket.

Note: This is similar to send in POSIX, though it also supports writing the data from multiple buffers in the manner of writev.

Inputs:

Outputs:

uvwasi_sock_shutdown()

Shut down socket send and receive channels.

Note: This is similar to shutdown in POSIX.

Inputs:

Types

uvwasi_advice_t (uint8_t)

File or memory access pattern advisory information.

Used by uvwasi_fd_advise().

Possible values:

uvwasi_ciovec_t (struct)

A region of memory for scatter/gather writes.

Used by uvwasi_fd_pwrite(), uvwasi_fd_write(), and uvwasi_sock_send().

Members:

uvwasi_clockid_t (uint32_t)

Identifiers for clocks.

Used by uvwasi_subscription_t, uvwasi_clock_res_get(), and uvwasi_clock_time_get().

Possible values:

uvwasi_device_t (uint64_t)

Identifier for a device containing a file system. Can be used in combination with uvwasi_inode_t to uniquely identify a file or directory in the filesystem.

Used by uvwasi_filestat_t.

uvwasi_dircookie_t (uint64_t)

A reference to the offset of a directory entry.

Used by uvwasi_dirent_t and uvwasi_fd_readdir().

Special values:

uvwasi_dirent_t (struct)

A directory entry.

Members:

uvwasi_errno_t (uint16_t)

Error codes returned by functions.

Not all of these error codes are returned by the functions provided by this API; some are used in higher-level library layers, and others are provided merely for alignment with POSIX.

Used by uvwasi_event_t.

Possible values:

uvwasi_event_t (struct)

An event that occurred.

Used by uvwasi_poll_oneoff().

Members:

uvwasi_eventrwflags_t (uint16_t bitfield)

The state of the file descriptor subscribed to with UVWASI_EVENTTYPE_FD_READ or UVWASI_EVENTTYPE_FD_WRITE.

Used by uvwasi_event_t.

Possible values:

uvwasi_eventtype_t (uint8_t)

Type of a subscription to an event or its occurrence.

Used by uvwasi_event_t and uvwasi_subscription_t.

Possible values:

uvwasi_exitcode_t (uint32_t)

Exit code generated by a process when exiting.

Used by uvwasi_proc_exit().

uvwasi_fd_t (uint32_t)

A file descriptor number.

Used by many functions in this API.

As in POSIX, three file descriptor numbers are provided to instances on startup -- 0, 1, and 2, (a.k.a. STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO).

Other than these, WASI implementations are not required to allocate new file descriptors in ascending order.

uvwasi_fdflags_t (uint16_t bitfield)

File descriptor flags.

Used by uvwasi_fdstat_t, uvwasi_fd_fdstat_set_flags(), and uvwasi_path_open().

Possible values:

uvwasi_fdstat_t (struct)

File descriptor attributes.

Used by uvwasi_fd_fdstat_get().

Members:

uvwasi_filedelta_t (int64_t)

Relative offset within a file.

Used by uvwasi_fd_seek().

uvwasi_filesize_t (uint64_t)

Non-negative file size or length of a region within a file.

Used by uvwasi_event_t, uvwasi_filestat_t, uvwasi_fd_pread(), uvwasi_fd_pwrite(), uvwasi_fd_seek(), uvwasi_path_tell(), uvwasi_fd_advise(), uvwasi_fd_allocate(), and uvwasi_fd_filestat_set_size().

uvwasi_filestat_t (struct)

File attributes.

Used by uvwasi_fd_filestat_get() and uvwasi_path_filestat_get().

Members:

uvwasi_filetype_t (uint8_t)

The type of a file descriptor or file.

Used by uvwasi_dirent_t, uvwasi_fdstat_t, and uvwasi_filestat_t.

Possible values:

uvwasi_fstflags_t (uint16_t bitfield)

Which file time attributes to adjust.

Used by uvwasi_fd_filestat_set_times() and uvwasi_path_filestat_set_times().

Possible values:

uvwasi_inode_t (uint64_t)

File serial number that is unique within its file system.

Used by uvwasi_dirent_t and uvwasi_filestat_t.

uvwasi_iovec_t (struct)

A region of memory for scatter/gather reads.

Used by uvwasi_fd_pread(), uvwasi_fd_read(), and uvwasi_sock_recv().

Members:

uvwasi_linkcount_t (uint64_t)

Number of hard links to an inode.

Used by uvwasi_filestat_t.

uvwasi_lookupflags_t (uint32_t bitfield)

Flags determining the method of how paths are resolved.

Used by uvwasi_path_filestat_get(), uvwasi_path_filestat_set_times(), uvwasi_path_link(), and uvwasi_path_open().

Possible values:

uvwasi_oflags_t (uint16_t bitfield)

Open flags used by uvwasi_path_open().

Used by uvwasi_path_open().

Possible values:

uvwasi_riflags_t (uint16_t bitfield)

Flags provided to uvwasi_sock_recv().

Used by uvwasi_sock_recv().

Possible values:

uvwasi_rights_t (uint64_t bitfield)

File descriptor rights, determining which actions may be performed.

Used by uvwasi_fdstat_t, uvwasi_fd_fdstat_set_rights(), and uvwasi_path_open().

Possible values:

uvwasi_roflags_t (uint16_t bitfield)

Flags returned by uvwasi_sock_recv().

Used by uvwasi_sock_recv().

Possible values:

uvwasi_sdflags_t (uint8_t bitfield)

Which channels on a socket to shut down.

Used by uvwasi_sock_shutdown().

Possible values:

uvwasi_siflags_t (uint16_t bitfield)

Flags provided to uvwasi_sock_send(). As there are currently no flags defined, it must be set to zero.

Used by uvwasi_sock_send().

uvwasi_signal_t (uint8_t)

Signal condition.

Used by uvwasi_proc_raise().

Possible values:

uvwasi_subclockflags_t (uint16_t bitfield)

Flags determining how to interpret the timestamp provided in uvwasi_subscription_t::u.clock.timeout.

Used by uvwasi_subscription_t.

Possible values:

uvwasi_subscription_t (struct)

Subscription to an event.

Used by uvwasi_poll_oneoff().

Members:

uvwasi_timestamp_t (uint64_t)

Timestamp in nanoseconds.

Used by uvwasi_filestat_t, uvwasi_subscription_t, uvwasi_clock_res_get(), uvwasi_clock_time_get(), uvwasi_fd_filestat_set_times(), and uvwasi_path_filestat_set_times().

uvwasi_userdata_t (uint64_t)

User-provided value that may be attached to objects that is retained when extracted from the implementation.

Used by uvwasi_event_t and uvwasi_subscription_t.

uvwasi_whence_t (uint8_t)

The position relative to which to set the offset of the file descriptor.

Used by uvwasi_fd_seek().

Possible values:

Doing a release

To do a release complete the following steps: