mkxp-z/binding-sandbox/wasi.h

262 lines
12 KiB
C++

/*
** wasi.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
**
** mkxp is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MKXPZ_SANDBOX_WASI_H
#define MKXPZ_SANDBOX_WASI_H
#include <memory>
#include <string>
#include <vector>
#include <priority_deque.hpp>
#include "filesystem.h"
#include "wasm-types.h"
#include "binding-base.h"
// WASI error numbers
#define WASI_ESUCCESS 0 // No error occurred. System call completed successfully.
#define WASI_E2BIG 1 // Argument list too long.
#define WASI_EACCES 2 // Permission denied.
#define WASI_EADDRINUSE 3 // Address in use.
#define WASI_EADDRNOTAVAIL 4 // Address not available.
#define WASI_EAFNOSUPPORT 5 // Address family not supported.
#define WASI_EAGAIN 6 // Resource unavailable, or operation would block.
#define WASI_EALREADY 7 // Connection already in progress.
#define WASI_EBADF 8 // Bad file descriptor.
#define WASI_EBADMSG 9 // Bad message.
#define WASI_EBUSY 10 // Device or resource busy.
#define WASI_ECANCELED 11 // Operation canceled.
#define WASI_ECHILD 12 // No child processes.
#define WASI_ECONNABORTED 13 // Connection aborted.
#define WASI_ECONNREFUSED 14 // Connection refused.
#define WASI_ECONNRESET 15 // Connection reset.
#define WASI_EDEADLK 16 // Resource deadlock would occur.
#define WASI_EDESTADDRREQ 17 // Destination address required.
#define WASI_EDOM 18 // Mathematics argument out of domain of function.
#define WASI_EDQUOT 19 // Reserved.
#define WASI_EEXIST 20 // File exists.
#define WASI_EFAULT 21 // Bad address.
#define WASI_EFBIG 22 // File too large.
#define WASI_EHOSTUNREACH 23 // Host is unreachable.
#define WASI_EIDRM 24 // Identifier removed.
#define WASI_EILSEQ 25 // Illegal byte sequence.
#define WASI_EINPROGRESS 26 // Operation in progress.
#define WASI_EINTR 27 // Interrupted function.
#define WASI_EINVAL 28 // Invalid argument.
#define WASI_EIO 29 // I/O error.
#define WASI_EISCONN 30 // Socket is connected.
#define WASI_EISDIR 31 // Is a directory.
#define WASI_ELOOP 32 // Too many levels of symbolic links.
#define WASI_EMFILE 33 // File descriptor value too large.
#define WASI_EMLINK 34 // Too many links.
#define WASI_EMSGSIZE 35 // Message too large.
#define WASI_EMULTIHOP 36 // Reserved.
#define WASI_ENAMETOOLONG 37 // Filename too long.
#define WASI_ENETDOWN 38 // Network is down.
#define WASI_ENETRESET 39 // Connection aborted by network.
#define WASI_ENETUNREACH 40 // Network unreachable.
#define WASI_ENFILE 41 // Too many files open in system.
#define WASI_ENOBUFS 42 // No buffer space available.
#define WASI_ENODEV 43 // No such device.
#define WASI_ENOENT 44 // No such file or directory.
#define WASI_ENOEXEC 45 // Executable file format error.
#define WASI_ENOLCK 46 // No locks available.
#define WASI_ENOLINK 47 // Reserved.
#define WASI_ENOMEM 48 // Not enough space.
#define WASI_ENOMSG 49 // No message of the desired type.
#define WASI_ENOPROTOOPT 50 // Protocol not available.
#define WASI_ENOSPC 51 // No space left on device.
#define WASI_ENOSYS 52 // Function not supported.
#define WASI_ENOTCONN 53 // The socket is not connected.
#define WASI_ENOTDIR 54 // Not a directory or a symbolic link to a directory.
#define WASI_ENOTEMPTY 55 // Directory not empty.
#define WASI_ENOTRECOVERABLE 56 // State not recoverable.
#define WASI_ENOTSOCK 57 // Not a socket.
#define WASI_ENOTSUP 58 // Not supported, or operation not supported on socket.
#define WASI_ENOSTDIO 59 // Inappropriate I/O control operation.
#define WASI_ENXIO 60 // No such device or address.
#define WASI_EOVERFLOW 61 // Value too large to be stored in data type.
#define WASI_EOWNERDEAD 62 // Previous owner died.
#define WASI_EPERM 63 // Operation not permitted.
#define WASI_EPIPE 64 // Broken pipe.
#define WASI_EPROTO 65 // Protocol error.
#define WASI_EPROTONOSUPPORT 66 // Protocol not supported.
#define WASI_EPROTOTYPE 67 // Protocol wrong type for socket.
#define WASI_ERANGE 68 // Result too large.
#define WASI_EROFS 69 // Read-only file system.
#define WASI_ESPIPE 70 // Invalid seek.
#define WASI_ESRCH 71 // No such process.
#define WASI_ESTALE 72 // Reserved.
#define WASI_ETIMEDOUT 73 // Connection timed out.
#define WASI_ETXTBSY 74 // Text file busy.
#define WASI_EXDEV 75 // Cross-device link.
#define WASI_ENOTCAPABLE 76 // Extension: Capabilities insufficient.
// WASI file types
#define WASI_IFUNK 0 // Unknown
#define WASI_IFBLK 1 // Block device
#define WASI_IFCHR 2 // Character device
#define WASI_IFDIR 3 // Directory
#define WASI_IFREG 4 // Regular file
#define WASI_IFSOCKD 5 // Datagram socket
#define WASI_IFSOCKS 6 // Stream socket
#define WASI_IFLNK 7 // Symbolic link
// WASI file flags
#define WASI_APPEND (1 << 0)
#define WASI_DSYNC (1 << 1)
#define WASI_NONBLOCK (1 << 2)
#define WASI_RSYNC (1 << 3)
#define WASI_SYNC (1 << 4)
// WASI rights flags
#define WASI_FD_DATASYNC (1 << 0)
#define WASI_FD_READ (1 << 1)
#define WASI_FD_SEEK (1 << 2)
#define WASI_FD_FDSTAT_SET_FLAGS (1 << 3)
#define WASI_FD_SYNC (1 << 4)
#define WASI_FD_TELL (1 << 5)
#define WASI_FD_WRITE (1 << 6)
#define WASI_FD_ADVISE (1 << 7)
#define WASI_FD_ALLOCATE (1 << 8)
#define WASI_PATH_CREATE_DIRECTORY (1 << 9)
#define WASI_PATH_CREATE_FILE (1 << 10)
#define WASI_PATH_LINK_SOURCE (1 << 11)
#define WASI_PATH_LINK_TARGET (1 << 12)
#define WASI_PATH_OPEN (1 << 13)
#define WASI_FD_READDIR (1 << 14)
#define WASI_PATH_READLINK (1 << 15)
#define WASI_PATH_RENAME_SOURCE (1 << 16)
#define WASI_PATH_RENAME_TARGET (1 << 17)
#define WASI_PATH_FILESTAT_GET (1 << 18)
#define WASI_PATH_FILESTAT_SET_SIZE (1 << 19)
#define WASI_PATH_FILESTAT_SET_TIMES (1 << 20)
#define WASI_FD_FILESTAT_GET (1 << 21)
#define WASI_FD_FILESTAT_SET_SIZE (1 << 22)
#define WASI_FD_FILESTAT_SET_TIMES (1 << 23)
#define WASI_PATH_SYMLINK (1 << 24)
#define WASI_PATH_REMOVE_DIRECTORY (1 << 25)
#define WASI_PATH_UNLINK_FILE (1 << 26)
#define WASI_POLL_FD_READWRITE (1 << 27)
#define WASI_SOCK_SHUTDOWN (1 << 28)
#define WASI_SOCK_ACCEPT (1 << 29)
typedef std::pair<uint32_t, std::string> path_cache_entry_t;
struct fs_dir {
std::string path; // Path of the directory.
uint32_t root; // Undefined if this is a preopened directory, otherwise the file descriptor of the preopened directory that contains this directory.
bool writable; // If true, writes made to this directory handle will be routed into the save directory. Otherwise, writes are disallowed.
};
struct fs_file {
struct FileSystem::File file;
uint32_t root; // The file descriptor of the preopened directory that contains this file.
};
enum wasi_fd_type {
STDIN, // This file descriptor is standard input. The `handle` field is null.
STDOUT, // This file descriptor is standard output. The `handle` field is null.
STDERR, // This file descriptor is standard error. The `handle` field is null.
FS, // This file descriptor is a preopened directory handled by PhysFS. The `handle` field is a `struct fs_dir *`.
FSDIR, // This file descriptor is a directory handled by PhysFS. The `handle` field is a `struct fs_dir *`.
FSFILE, // This file descriptor is a file handled by PhysFS. The `handle` field is a `struct fs_file *`.
VACANT, // Indicates this is a vacant file descriptor that doesn't correspond to a file. The `handle` field is null.
};
struct wasi_file_entry {
// The file/directory handle that the file descriptor corresponds to. The exact type of this handle depends on the type of file descriptor.
void *handle;
wasi_fd_type type;
struct fs_dir *dir_handle() const noexcept;
struct fs_file *file_handle() const noexcept;
};
typedef struct w2c_wasi__snapshot__preview1 {
std::shared_ptr<struct w2c_ruby> ruby;
// WASI file descriptor table. Maps WASI file descriptors (unsigned 32-bit integers) to file handles.
std::vector<wasi_file_entry> fdtable;
// List of vacant WASI file descriptors so that we can reallocate vacant WASI file descriptors quickly.
boost::container::priority_deque<uint32_t> vacant_fds;
uint64_t prng_state;
uint8_t prng_buffer[4];
uint32_t prng_buffer_size;
w2c_wasi__snapshot__preview1(std::shared_ptr<struct w2c_ruby> ruby);
~w2c_wasi__snapshot__preview1();
uint32_t allocate_file_descriptor(enum wasi_fd_type type, void *handle = nullptr);
void deallocate_file_descriptor(uint32_t fd);
// Gets a pointer to the given address in sandbox memory.
// Unlike `sandbox_ref`, the address does not need to be aligned.
template <typename T> void *ptr_unaligned(mkxp_sandbox::wasm_ptr_t address) const noexcept {
return mkxp_sandbox::sandbox_ptr_unaligned<T>(*ruby, address);
}
// Gets a pointer to the given index in the array at a given address in sandbox memory.
// Unlike `sandbox_ref`, the address does not need to be aligned.
template <typename T> void *ptr_unaligned(mkxp_sandbox::wasm_ptr_t array_address, mkxp_sandbox::wasm_size_t array_index) const noexcept {
return mkxp_sandbox::sandbox_ptr_unaligned<T>(*ruby, array_address, array_index);
}
// Gets a reference to the value stored at a given address in sandbox memory.
// Make sure the address is aligned, or this function will abort.
template <typename T> T &ref(mkxp_sandbox::wasm_ptr_t address) const noexcept {
return mkxp_sandbox::sandbox_ref<T>(*ruby, address);
}
// Gets a reference to the value stored at the given index in the array at a given address in sandbox memory.
// Make sure the address is aligned, or this function will abort.
template <typename T> T &ref(mkxp_sandbox::wasm_ptr_t array_address, mkxp_sandbox::wasm_size_t array_index) const noexcept {
return ref<T>(array_address + array_index * sizeof(T));
}
// Checks if the array with the given address and size in bytes is within the bounds of sandbox memory.
// If it isn't, aborts. Otherwise, does nothing.
void check_bounds(mkxp_sandbox::wasm_ptr_t address, mkxp_sandbox::wasm_size_t size) const noexcept;
// Gets a string stored at a given address in sandbox memory.
struct mkxp_sandbox::sandbox_str_guard str(mkxp_sandbox::wasm_ptr_t address) const noexcept;
// Gets the length of a string stored at a given address in sandbox memory.
mkxp_sandbox::wasm_size_t strlen(mkxp_sandbox::wasm_ptr_t address) const noexcept;
// Copies a string into a sandbox memory address.
void strcpy(mkxp_sandbox::wasm_ptr_t dst_address, const char *src) const noexcept;
// Copies a string into a sandbox memory address.
void strncpy_s(mkxp_sandbox::wasm_ptr_t dst_address, const char *src, mkxp_sandbox::wasm_size_t max_size) const noexcept;
// Copies an array of length `num_elements` into a sandbox memory address.
template <typename T> void arycpy(mkxp_sandbox::wasm_ptr_t dst_address, const T *src, mkxp_sandbox::wasm_size_t num_elements) const noexcept {
mkxp_sandbox::sandbox_arycpy(*ruby, dst_address, src, num_elements);
}
bool sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size) const;
bool sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size);
} wasi_t;
#endif /* MKXPZ_SANDBOX_WASI_H */