summaryrefslogtreecommitdiff
path: root/deps/uvwasi
diff options
context:
space:
mode:
authorcjihrig <cjihrig@gmail.com>2020-04-26 10:57:44 -0400
committercjihrig <cjihrig@gmail.com>2020-04-28 13:08:45 -0400
commit497ad815aee22a70121ca6d51d69bc0bcc923fa3 (patch)
treee126b9f8b4ec9a7fa5058d3666de6777085cb785 /deps/uvwasi
parent6ca6db105d8db40af538e275f39612b569b9c141 (diff)
downloadnode-new-497ad815aee22a70121ca6d51d69bc0bcc923fa3.tar.gz
deps: update to uvwasi 0.0.8
This release focuses on improving the robustness of the path resolution and sandboxing, including adding support for relative preopen paths. PR-URL: https://github.com/nodejs/node/pull/33078 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'deps/uvwasi')
-rw-r--r--deps/uvwasi/include/fd_table.h1
-rw-r--r--deps/uvwasi/include/uvwasi.h6
-rw-r--r--deps/uvwasi/include/wasi_types.h2
-rw-r--r--deps/uvwasi/src/clocks.c4
-rw-r--r--deps/uvwasi/src/fd_table.c19
-rw-r--r--deps/uvwasi/src/path_resolver.c492
-rw-r--r--deps/uvwasi/src/path_resolver.h28
-rw-r--r--deps/uvwasi/src/uvwasi.c330
-rw-r--r--deps/uvwasi/uvwasi.gyp1
9 files changed, 546 insertions, 337 deletions
diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h
index f29b1adf88..474a0231e0 100644
--- a/deps/uvwasi/include/fd_table.h
+++ b/deps/uvwasi/include/fd_table.h
@@ -13,6 +13,7 @@ struct uvwasi_fd_wrap_t {
uv_file fd;
char* path;
char* real_path;
+ char* normalized_path;
uvwasi_filetype_t type;
uvwasi_rights_t rights_base;
uvwasi_rights_t rights_inheriting;
diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h
index 39ee2f0ceb..f2d2d1a9fd 100644
--- a/deps/uvwasi/include/uvwasi.h
+++ b/deps/uvwasi/include/uvwasi.h
@@ -11,7 +11,7 @@ extern "C" {
#define UVWASI_VERSION_MAJOR 0
#define UVWASI_VERSION_MINOR 0
-#define UVWASI_VERSION_PATCH 6
+#define UVWASI_VERSION_PATCH 8
#define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \
(UVWASI_VERSION_MINOR << 8) | \
(UVWASI_VERSION_PATCH))
@@ -66,7 +66,7 @@ typedef struct uvwasi_options_s {
const uvwasi_mem_t* allocator;
} uvwasi_options_t;
-// Embedder API.
+/* Embedder API. */
uvwasi_errno_t uvwasi_init(uvwasi_t* uvwasi, uvwasi_options_t* options);
void uvwasi_destroy(uvwasi_t* uvwasi);
uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi,
@@ -75,7 +75,7 @@ uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi,
const char* uvwasi_embedder_err_code_to_string(uvwasi_errno_t code);
-// WASI system call API.
+/* WASI system call API. */
uvwasi_errno_t uvwasi_args_get(uvwasi_t* uvwasi, char** argv, char* argv_buf);
uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi,
size_t* argc,
diff --git a/deps/uvwasi/include/wasi_types.h b/deps/uvwasi/include/wasi_types.h
index ec1013663f..2f93b41262 100644
--- a/deps/uvwasi/include/wasi_types.h
+++ b/deps/uvwasi/include/wasi_types.h
@@ -4,7 +4,7 @@
#include <stddef.h>
#include <stdint.h>
-/* API: https://github.com/WebAssembly/WASI/blob/master/phases/unstable/docs/wasi_unstable_preview0.md */
+/* API: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md */
typedef uint8_t uvwasi_advice_t;
#define UVWASI_ADVICE_NORMAL 0
diff --git a/deps/uvwasi/src/clocks.c b/deps/uvwasi/src/clocks.c
index fd42b9e50e..b59cbd6bb1 100644
--- a/deps/uvwasi/src/clocks.c
+++ b/deps/uvwasi/src/clocks.c
@@ -153,7 +153,7 @@ uvwasi_errno_t uvwasi__clock_gettime_thread_cputime(uvwasi_timestamp_t* time) {
UVWASI__WIN_TIME_AND_RETURN(GetCurrentThread(), *time);
#elif defined(__APPLE__)
UVWASI__OSX_THREADTIME_AND_RETURN(*time);
-#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun)
+#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) && !defined(__PASE__)
UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time);
#else
# if defined(RUSAGE_LWP)
@@ -185,7 +185,7 @@ uvwasi_errno_t uvwasi__clock_getres_thread_cputime(uvwasi_timestamp_t* time) {
UVWASI__WIN_GETRES_AND_RETURN(*time);
#elif defined(__APPLE__)
UVWASI__SLOW_GETRES_AND_RETURN(*time);
-#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun)
+#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) && !defined(__PASE__)
UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time);
#elif defined(RUSAGE_THREAD) || defined(RUSAGE_LWP)
UVWASI__SLOW_GETRES_AND_RETURN(*time);
diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c
index f6e530d959..3d134e3b7e 100644
--- a/deps/uvwasi/src/fd_table.c
+++ b/deps/uvwasi/src/fd_table.c
@@ -8,6 +8,7 @@
#include "uv.h"
#include "fd_table.h"
+#include "path_resolver.h"
#include "wasi_types.h"
#include "wasi_rights.h"
#include "uv_mapping.h"
@@ -75,20 +76,33 @@ uvwasi_errno_t uvwasi_fd_table_insert(uvwasi_t* uvwasi,
char* mp_copy;
size_t rp_len;
char* rp_copy;
+ char* np_copy;
mp_len = strlen(mapped_path);
rp_len = strlen(real_path);
+ /* Reserve room for the mapped path, real path, and normalized mapped path. */
entry = (struct uvwasi_fd_wrap_t*)
- uvwasi__malloc(uvwasi, sizeof(*entry) + mp_len + rp_len + 2);
- if (entry == NULL) return UVWASI_ENOMEM;
+ uvwasi__malloc(uvwasi, sizeof(*entry) + mp_len + mp_len + rp_len + 3);
+ if (entry == NULL)
+ return UVWASI_ENOMEM;
mp_copy = (char*)(entry + 1);
rp_copy = mp_copy + mp_len + 1;
+ np_copy = rp_copy + rp_len + 1;
memcpy(mp_copy, mapped_path, mp_len);
mp_copy[mp_len] = '\0';
memcpy(rp_copy, real_path, rp_len);
rp_copy[rp_len] = '\0';
+ /* Calculate the normalized version of the mapped path, as it will be used for
+ any path calculations on this fd. Use the length of the mapped path as an
+ upper bound for the normalized path length. */
+ err = uvwasi__normalize_path(mp_copy, mp_len, np_copy, mp_len);
+ if (err) {
+ uvwasi__free(uvwasi, entry);
+ goto exit;
+ }
+
uv_rwlock_wrlock(&table->rwlock);
/* Check that there is room for a new item. If there isn't, grow the table. */
@@ -138,6 +152,7 @@ uvwasi_errno_t uvwasi_fd_table_insert(uvwasi_t* uvwasi,
entry->fd = fd;
entry->path = mp_copy;
entry->real_path = rp_copy;
+ entry->normalized_path = np_copy;
entry->type = type;
entry->rights_base = rights_base;
entry->rights_inheriting = rights_inheriting;
diff --git a/deps/uvwasi/src/path_resolver.c b/deps/uvwasi/src/path_resolver.c
new file mode 100644
index 0000000000..ee0e60f7e8
--- /dev/null
+++ b/deps/uvwasi/src/path_resolver.c
@@ -0,0 +1,492 @@
+#include <string.h>
+
+#include "uv.h"
+#include "uvwasi.h"
+#include "uvwasi_alloc.h"
+#include "uv_mapping.h"
+#include "path_resolver.h"
+
+#define UVWASI__MAX_SYMLINK_FOLLOWS 32
+
+#ifndef _WIN32
+# define IS_SLASH(c) ((c) == '/')
+#else
+# define IS_SLASH(c) ((c) == '/' || (c) == '\\')
+#endif /* _WIN32 */
+
+
+static int uvwasi__is_absolute_path(const char* path, size_t path_len) {
+ /* It's expected that only Unix style paths will be generated by WASI. */
+ return path != NULL && path_len > 0 && path[0] == '/';
+}
+
+
+static char* uvwasi__strchr_slash(const char* s) {
+ /* strchr() that identifies /, as well as \ on Windows. */
+ do {
+ if (IS_SLASH(*s))
+ return (char*) s;
+ } while (*s++);
+
+ return NULL;
+}
+
+
+uvwasi_errno_t uvwasi__normalize_path(const char* path,
+ size_t path_len,
+ char* normalized_path,
+ size_t normalized_len) {
+ const char* cur;
+ char* ptr;
+ char* next;
+ char* last;
+ size_t cur_len;
+ int is_absolute;
+
+ if (path_len > normalized_len)
+ return UVWASI_ENOBUFS;
+
+ is_absolute = uvwasi__is_absolute_path(path, path_len);
+ normalized_path[0] = '\0';
+ ptr = normalized_path;
+ for (cur = path; cur != NULL; cur = next + 1) {
+ next = uvwasi__strchr_slash(cur);
+ cur_len = (next == NULL) ? strlen(cur) : (size_t) (next - cur);
+
+ if (cur_len == 0) {
+ if (ptr == normalized_path && next != NULL && is_absolute) {
+ *ptr = '/';
+ ptr++;
+ }
+
+ *ptr = '\0';
+ } else if (cur_len == 1 && cur[0] == '.') {
+ /* No-op. Just consume the '.' */
+ } else if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
+ /* Identify the path segment that preceded the current one. */
+ last = ptr;
+ while (!IS_SLASH(*last) && last != normalized_path) {
+ last--;
+ }
+
+ /* If the result is currently empty, or the last prior path is also '..'
+ then output '..'. Otherwise, remove the last path segment. */
+ if (ptr == normalized_path ||
+ (last == ptr - 2 && last[0] == '.' && last[1] == '.') ||
+ (last == ptr - 3 && last[0] == '/' &&
+ last[1] == '.' && last[2] == '.')) {
+ if (ptr != normalized_path && *(ptr - 1) != '/') {
+ *ptr = '/';
+ ptr++;
+ }
+
+ *ptr = '.';
+ ptr++;
+ *ptr = '.';
+ ptr++;
+ } else {
+ /* Strip the last segment, but make sure not to strip the '/' if that
+ is the entire path. */
+ if (last == normalized_path && *last == '/')
+ ptr = last + 1;
+ else
+ ptr = last;
+ }
+
+ *ptr = '\0';
+ } else {
+ if (ptr != normalized_path && *(ptr - 1) != '/') {
+ *ptr = '/';
+ ptr++;
+ }
+
+ memcpy(ptr, cur, cur_len);
+ ptr += cur_len;
+ *ptr = '\0';
+ }
+
+ if (next == NULL)
+ break;
+ }
+
+ /* Normalized the path to the empty string. Return either '/' or '.'. */
+ if (ptr == normalized_path) {
+ if (1 == is_absolute)
+ *ptr = '/';
+ else
+ *ptr = '.';
+
+ ptr++;
+ *ptr = '\0';
+ }
+
+ return UVWASI_ESUCCESS;
+}
+
+
+static int uvwasi__is_path_sandboxed(const char* path,
+ size_t path_len,
+ const char* fd_path,
+ size_t fd_path_len) {
+ char* ptr;
+ int remaining_len;
+
+ if (1 == uvwasi__is_absolute_path(fd_path, fd_path_len))
+ return path == strstr(path, fd_path) ? 1 : 0;
+
+ /* Handle relative fds that normalized to '.' */
+ if (fd_path_len == 1 && fd_path[0] == '.') {
+ /* If the fd's path is '.', then any path does not begin with '..' is OK. */
+ if ((path_len == 2 && path[0] == '.' && path[1] == '.') ||
+ (path_len > 2 && path[0] == '.' && path[1] == '.' && path[2] == '/')) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ if (path != strstr(path, fd_path))
+ return 0;
+
+ /* Fail if the remaining path starts with '..', '../', '/..', or '/../'. */
+ ptr = (char*) path + fd_path_len;
+ remaining_len = path_len - fd_path_len;
+ if (remaining_len < 2)
+ return 1;
+
+ /* Strip a leading slash so the check is only for '..' and '../'. */
+ if (*ptr == '/') {
+ ptr++;
+ remaining_len--;
+ }
+
+ if ((remaining_len == 2 && ptr[0] == '.' && ptr[1] == '.') ||
+ (remaining_len > 2 && ptr[0] == '.' && ptr[1] == '.' && ptr[2] == '/')) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static uvwasi_errno_t uvwasi__normalize_absolute_path(
+ const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char** normalized_path,
+ size_t* normalized_len
+ ) {
+ /* This function resolves an absolute path to the provided file descriptor.
+ If the file descriptor's path is relative, then this operation will fail
+ with UVWASI_ENOTCAPABLE since it doesn't make sense to resolve an absolute
+ path to a relative prefix. If the file desciptor's path is also absolute,
+ then we just need to verify that the normalized path still starts with
+ the file descriptor's path. */
+ uvwasi_errno_t err;
+ char* abs_path;
+ int abs_size;
+
+ *normalized_path = NULL;
+ *normalized_len = 0;
+ abs_size = path_len + 1;
+ abs_path = uvwasi__malloc(uvwasi, abs_size);
+ if (abs_path == NULL) {
+ err = UVWASI_ENOMEM;
+ goto exit;
+ }
+
+ /* Normalize the input path first. */
+ err = uvwasi__normalize_path(path, path_len, abs_path, path_len);
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ /* Once the input is normalized, ensure that it is still sandboxed. */
+ if (0 == uvwasi__is_path_sandboxed(abs_path,
+ path_len,
+ fd->normalized_path,
+ strlen(fd->normalized_path))) {
+ err = UVWASI_ENOTCAPABLE;
+ goto exit;
+ }
+
+ *normalized_path = abs_path;
+ *normalized_len = abs_size - 1;
+ return UVWASI_ESUCCESS;
+
+exit:
+ uvwasi__free(uvwasi, abs_path);
+ return err;
+}
+
+
+static uvwasi_errno_t uvwasi__normalize_relative_path(
+ const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char** normalized_path,
+ size_t* normalized_len
+ ) {
+ /* This function resolves a relative path to the provided file descriptor.
+ The relative path is concatenated to the file descriptor's path, and then
+ normalized. */
+ uvwasi_errno_t err;
+ char* combined;
+ char* normalized;
+ int combined_size;
+ int fd_path_len;
+ int norm_len;
+ int r;
+
+ *normalized_path = NULL;
+ *normalized_len = 0;
+
+ /* The max combined size is the path length + the file descriptor's path
+ length + 2 for a terminating NULL and a possible path separator. */
+ fd_path_len = strlen(fd->normalized_path);
+ combined_size = path_len + fd_path_len + 2;
+ combined = uvwasi__malloc(uvwasi, combined_size);
+ if (combined == NULL)
+ return UVWASI_ENOMEM;
+
+ normalized = uvwasi__malloc(uvwasi, combined_size);
+ if (normalized == NULL) {
+ err = UVWASI_ENOMEM;
+ goto exit;
+ }
+
+ r = snprintf(combined, combined_size, "%s/%s", fd->normalized_path, path);
+ if (r <= 0) {
+ err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
+ goto exit;
+ }
+
+ /* Normalize the input path. */
+ err = uvwasi__normalize_path(combined,
+ combined_size - 1,
+ normalized,
+ combined_size - 1);
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ norm_len = strlen(normalized);
+
+ /* Once the path is normalized, ensure that it is still sandboxed. */
+ if (0 == uvwasi__is_path_sandboxed(normalized,
+ norm_len,
+ fd->normalized_path,
+ fd_path_len)) {
+ err = UVWASI_ENOTCAPABLE;
+ goto exit;
+ }
+
+ err = UVWASI_ESUCCESS;
+ *normalized_path = normalized;
+ *normalized_len = norm_len;
+
+exit:
+ if (err != UVWASI_ESUCCESS)
+ uvwasi__free(uvwasi, normalized);
+
+ uvwasi__free(uvwasi, combined);
+ return err;
+}
+
+
+static uvwasi_errno_t uvwasi__resolve_path_to_host(
+ const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char** resolved_path,
+ size_t* resolved_len
+ ) {
+ /* Return the normalized path, but resolved to the host's real path. */
+ char* res_path;
+ char* stripped_path;
+ int real_path_len;
+ int fake_path_len;
+ int stripped_len;
+#ifdef _WIN32
+ size_t i;
+#endif /* _WIN32 */
+
+ real_path_len = strlen(fd->real_path);
+ fake_path_len = strlen(fd->normalized_path);
+
+ /* If the fake path is '.' just ignore it. */
+ if (fake_path_len == 1 && fd->normalized_path[0] == '.') {
+ fake_path_len = 0;
+ }
+
+ stripped_len = path_len - fake_path_len;
+
+ /* The resolved path's length is calculated as: the length of the fd's real
+ path, + 1 for a path separator, and the length of the input path (with the
+ fake path stripped off). */
+ *resolved_len = stripped_len + real_path_len + 1;
+ *resolved_path = uvwasi__malloc(uvwasi, *resolved_len + 1);
+
+ if (*resolved_path == NULL)
+ return UVWASI_ENOMEM;
+
+ res_path = *resolved_path;
+ stripped_path = (char*) path + fake_path_len;
+ memcpy(res_path, fd->real_path, real_path_len);
+ res_path += real_path_len;
+
+ if (stripped_len > 1 ||
+ (stripped_len == 1 && stripped_path[0] != '/')) {
+ if (stripped_path[0] != '/') {
+ *res_path = '/';
+ res_path++;
+ }
+
+ memcpy(res_path, stripped_path, stripped_len);
+ res_path += stripped_len;
+ }
+
+ *res_path = '\0';
+
+#ifdef _WIN32
+ /* Replace / with \ on Windows. */
+ for (i = real_path_len; i < *resolved_len; i++) {
+ if (res_path[i] == '/')
+ res_path[i] = '\\';
+ }
+#endif /* _WIN32 */
+
+ return UVWASI_ESUCCESS;
+}
+
+
+uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char* resolved_path,
+ uvwasi_lookupflags_t flags) {
+ uv_fs_t req;
+ uvwasi_errno_t err;
+ const char* input;
+ char* host_path;
+ char* normalized_path;
+ char* link_target;
+ size_t input_len;
+ size_t host_path_len;
+ size_t normalized_len;
+ int follow_count;
+ int r;
+
+ input = path;
+ input_len = path_len;
+ link_target = NULL;
+ follow_count = 0;
+ host_path = NULL;
+
+start:
+ normalized_path = NULL;
+ err = UVWASI_ESUCCESS;
+
+ if (1 == uvwasi__is_absolute_path(input, input_len)) {
+ err = uvwasi__normalize_absolute_path(uvwasi,
+ fd,
+ input,
+ input_len,
+ &normalized_path,
+ &normalized_len);
+ } else {
+ err = uvwasi__normalize_relative_path(uvwasi,
+ fd,
+ input,
+ input_len,
+ &normalized_path,
+ &normalized_len);
+ }
+
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ uvwasi__free(uvwasi, host_path);
+ err = uvwasi__resolve_path_to_host(uvwasi,
+ fd,
+ normalized_path,
+ normalized_len,
+ &host_path,
+ &host_path_len);
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ /* TODO(cjihrig): Currently performing a bounds check here. The TODO is to
+ stop allocating resolved_path in every caller and instead return the
+ path allocated in this function. */
+ if (host_path_len > PATH_MAX_BYTES) {
+ err = UVWASI_ENOBUFS;
+ goto exit;
+ }
+
+ if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) {
+ r = uv_fs_readlink(NULL, &req, host_path, NULL);
+
+ if (r != 0) {
+#ifdef _WIN32
+ /* uv_fs_readlink() returns UV__UNKNOWN on Windows. Try to get a better
+ error using uv_fs_stat(). */
+ if (r == UV__UNKNOWN) {
+ uv_fs_req_cleanup(&req);
+ r = uv_fs_stat(NULL, &req, host_path, NULL);
+
+ if (r == 0) {
+ if (uvwasi__stat_to_filetype(&req.statbuf) !=
+ UVWASI_FILETYPE_SYMBOLIC_LINK) {
+ r = UV_EINVAL;
+ }
+ }
+
+ /* Fall through. */
+ }
+#endif /* _WIN32 */
+
+ /* Don't report UV_EINVAL or UV_ENOENT. They mean that either the file
+ does not exist, or it is not a symlink. Both are OK. */
+ if (r != UV_EINVAL && r != UV_ENOENT)
+ err = uvwasi__translate_uv_error(r);
+
+ uv_fs_req_cleanup(&req);
+ goto exit;
+ }
+
+ /* Clean up memory and follow the link, unless it's time to return ELOOP. */
+ follow_count++;
+ if (follow_count >= UVWASI__MAX_SYMLINK_FOLLOWS) {
+ uv_fs_req_cleanup(&req);
+ err = UVWASI_ELOOP;
+ goto exit;
+ }
+
+ input_len = strlen(req.ptr);
+ uvwasi__free(uvwasi, link_target);
+ link_target = uvwasi__malloc(uvwasi, input_len + 1);
+ if (link_target == NULL) {
+ uv_fs_req_cleanup(&req);
+ err = UVWASI_ENOMEM;
+ goto exit;
+ }
+
+ memcpy(link_target, req.ptr, input_len + 1);
+ input = link_target;
+ uvwasi__free(uvwasi, normalized_path);
+ uv_fs_req_cleanup(&req);
+ goto start;
+ }
+
+exit:
+ if (err == UVWASI_ESUCCESS)
+ memcpy(resolved_path, host_path, host_path_len + 1);
+
+ uvwasi__free(uvwasi, link_target);
+ uvwasi__free(uvwasi, normalized_path);
+ uvwasi__free(uvwasi, host_path);
+ return err;
+}
diff --git a/deps/uvwasi/src/path_resolver.h b/deps/uvwasi/src/path_resolver.h
new file mode 100644
index 0000000000..d5f95eafbf
--- /dev/null
+++ b/deps/uvwasi/src/path_resolver.h
@@ -0,0 +1,28 @@
+#ifndef __UVWASI_PATH_RESOLVER_H__
+#define __UVWASI_PATH_RESOLVER_H__
+
+#include "uvwasi.h"
+
+/* TODO(cjihrig): PATH_MAX_BYTES shouldn't be stack allocated. On Windows, paths
+ can be 32k long, and this PATH_MAX_BYTES is an artificial limitation. */
+#ifdef _WIN32
+/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
+# define PATH_MAX_BYTES (MAX_PATH * 4)
+#else
+# include <limits.h>
+# define PATH_MAX_BYTES (PATH_MAX)
+#endif
+
+uvwasi_errno_t uvwasi__normalize_path(const char* path,
+ size_t path_len,
+ char* normalized_path,
+ size_t normalized_len);
+
+uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char* resolved_path,
+ uvwasi_lookupflags_t flags);
+
+#endif /* __UVWASI_PATH_RESOLVER_H__ */
diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c
index c80fc7715c..0ee66be36a 100644
--- a/deps/uvwasi/src/uvwasi.c
+++ b/deps/uvwasi/src/uvwasi.c
@@ -7,14 +7,11 @@
# include <unistd.h>
# include <dirent.h>
# include <time.h>
-# define IS_SLASH(c) ((c) == '/')
#else
# include <io.h>
-# define IS_SLASH(c) ((c) == '/' || (c) == '\\')
#endif /* _WIN32 */
#define UVWASI__READDIR_NUM_ENTRIES 1
-#define UVWASI__MAX_SYMLINK_FOLLOWS 32
#include "uvwasi.h"
#include "uvwasi_alloc.h"
@@ -22,18 +19,9 @@
#include "uv_mapping.h"
#include "fd_table.h"
#include "clocks.h"
+#include "path_resolver.h"
#include "wasi_rights.h"
-/* TODO(cjihrig): PATH_MAX_BYTES shouldn't be stack allocated. On Windows, paths
- can be 32k long, and this PATH_MAX_BYTES is an artificial limitation. */
-#ifdef _WIN32
-/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
-# define PATH_MAX_BYTES (MAX_PATH * 4)
-#else
-# include <limits.h>
-# define PATH_MAX_BYTES (PATH_MAX)
-#endif
-
/* IBMi PASE does not support posix_fadvise() */
#ifdef __PASE__
# undef POSIX_FADV_NORMAL
@@ -84,322 +72,6 @@ static const uvwasi_mem_t default_allocator = {
};
-static int uvwasi__is_absolute_path(const char* path, size_t path_len) {
- /* It's expected that only Unix style paths will be generated by WASI. */
- return path != NULL && path_len > 0 && path[0] == '/';
-}
-
-
-static char* uvwasi__strchr_slash(const char* s) {
- /* strchr() that identifies /, as well as \ on Windows. */
- do {
- if (IS_SLASH(*s))
- return (char*) s;
- } while (*s++);
-
- return NULL;
-}
-
-
-static uvwasi_errno_t uvwasi__normalize_path(const char* path,
- size_t path_len,
- char* normalized_path,
- size_t normalized_len) {
- const char* cur;
- char* ptr;
- char* next;
- size_t cur_len;
-
- if (path_len > normalized_len)
- return UVWASI_ENOBUFS;
-
- normalized_path[0] = '\0';
- ptr = normalized_path;
- for (cur = path; cur != NULL; cur = next + 1) {
- next = uvwasi__strchr_slash(cur);
- cur_len = (next == NULL) ? strlen(cur) : (size_t) (next - cur);
-
- if (cur_len == 0 || (cur_len == 1 && cur[0] == '.'))
- continue;
-
- if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
- while (!IS_SLASH(*ptr) && ptr != normalized_path)
- ptr--;
- *ptr = '\0';
- continue;
- }
-
- *ptr = '/';
- ptr++;
- memcpy(ptr, cur, cur_len);
- ptr += cur_len;
- *ptr = '\0';
-
- if (next == NULL)
- break;
- }
-
- return UVWASI_ESUCCESS;
-}
-
-
-static uvwasi_errno_t uvwasi__resolve_path_to_host(
- const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char** resolved_path,
- size_t* resolved_len
- ) {
- /* Return the normalized path, but resolved to the host's real path. */
- int real_path_len;
- int fake_path_len;
-#ifdef _WIN32
- size_t i;
-#endif /* _WIN32 */
-
- real_path_len = strlen(fd->real_path);
- fake_path_len = strlen(fd->path);
- *resolved_len = path_len - fake_path_len + real_path_len;
- *resolved_path = uvwasi__malloc(uvwasi, *resolved_len + 1);
-
- if (*resolved_path == NULL)
- return UVWASI_ENOMEM;
-
- memcpy(*resolved_path, fd->real_path, real_path_len);
- memcpy(*resolved_path + real_path_len,
- path + fake_path_len,
- path_len - fake_path_len + 1);
-
-#ifdef _WIN32
- /* Replace / with \ on Windows. */
- for (i = real_path_len; i < *resolved_len; i++) {
- if ((*resolved_path)[i] == '/')
- (*resolved_path)[i] = '\\';
- }
-#endif /* _WIN32 */
-
- return UVWASI_ESUCCESS;
-}
-
-
-static uvwasi_errno_t uvwasi__normalize_absolute_path(
- const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char** normalized_path,
- size_t* normalized_len
- ) {
- uvwasi_errno_t err;
- char* abs_path;
- int abs_size;
-
- *normalized_path = NULL;
- *normalized_len = 0;
- abs_size = path_len + 1;
- abs_path = uvwasi__malloc(uvwasi, abs_size);
- if (abs_path == NULL) {
- err = UVWASI_ENOMEM;
- goto exit;
- }
-
- /* Normalize the input path first. */
- err = uvwasi__normalize_path(path,
- path_len,
- abs_path,
- path_len);
- if (err != UVWASI_ESUCCESS)
- goto exit;
-
- /* Once the input is normalized, ensure that it is still sandboxed. */
- if (abs_path != strstr(abs_path, fd->path)) {
- err = UVWASI_ENOTCAPABLE;
- goto exit;
- }
-
- *normalized_path = abs_path;
- *normalized_len = abs_size - 1;
- return UVWASI_ESUCCESS;
-
-exit:
- uvwasi__free(uvwasi, abs_path);
- return err;
-}
-
-
-static uvwasi_errno_t uvwasi__normalize_relative_path(
- const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char** normalized_path,
- size_t* normalized_len
- ) {
- uvwasi_errno_t err;
- char* abs_path;
- int abs_size;
- int r;
-
- *normalized_path = NULL;
- *normalized_len = 0;
- abs_size = path_len + strlen(fd->path) + 2;
- abs_path = uvwasi__malloc(uvwasi, abs_size);
- if (abs_path == NULL) {
- err = UVWASI_ENOMEM;
- goto exit;
- }
-
- /* Resolve the relative path to an absolute path based on fd's fake path. */
- r = snprintf(abs_path, abs_size, "%s/%s", fd->path, path);
- if (r <= 0) {
- err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
- goto exit;
- }
-
- err = uvwasi__normalize_absolute_path(uvwasi,
- fd,
- abs_path,
- abs_size - 1,
- normalized_path,
- normalized_len);
-exit:
- uvwasi__free(uvwasi, abs_path);
- return err;
-}
-
-
-static uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char* resolved_path,
- uvwasi_lookupflags_t flags) {
- uv_fs_t req;
- uvwasi_errno_t err;
- const char* input;
- char* host_path;
- char* normalized_path;
- char* link_target;
- size_t input_len;
- size_t host_path_len;
- size_t normalized_len;
- int follow_count;
- int r;
-
- input = path;
- input_len = path_len;
- link_target = NULL;
- follow_count = 0;
- host_path = NULL;
-
-start:
- normalized_path = NULL;
- err = UVWASI_ESUCCESS;
-
- if (1 == uvwasi__is_absolute_path(input, input_len)) {
- err = uvwasi__normalize_absolute_path(uvwasi,
- fd,
- input,
- input_len,
- &normalized_path,
- &normalized_len);
- } else {
- err = uvwasi__normalize_relative_path(uvwasi,
- fd,
- input,
- input_len,
- &normalized_path,
- &normalized_len);
- }
-
- if (err != UVWASI_ESUCCESS)
- goto exit;
-
- uvwasi__free(uvwasi, host_path);
- err = uvwasi__resolve_path_to_host(uvwasi,
- fd,
- normalized_path,
- normalized_len,
- &host_path,
- &host_path_len);
- if (err != UVWASI_ESUCCESS)
- goto exit;
-
- /* TODO(cjihrig): Currently performing a bounds check here. The TODO is to
- stop allocating resolved_path in every caller and instead return the
- path allocated in this function. */
- if (host_path_len > PATH_MAX_BYTES) {
- err = UVWASI_ENOBUFS;
- goto exit;
- }
-
- if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) {
- r = uv_fs_readlink(NULL, &req, host_path, NULL);
-
- if (r != 0) {
-#ifdef _WIN32
- /* uv_fs_readlink() returns UV__UNKNOWN on Windows. Try to get a better
- error using uv_fs_stat(). */
- if (r == UV__UNKNOWN) {
- uv_fs_req_cleanup(&req);
- r = uv_fs_stat(NULL, &req, host_path, NULL);
-
- if (r == 0) {
- if (uvwasi__stat_to_filetype(&req.statbuf) !=
- UVWASI_FILETYPE_SYMBOLIC_LINK) {
- r = UV_EINVAL;
- }
- }
-
- // Fall through.
- }
-#endif /* _WIN32 */
-
- /* Don't report UV_EINVAL or UV_ENOENT. They mean that either the file
- does not exist, or it is not a symlink. Both are OK. */
- if (r != UV_EINVAL && r != UV_ENOENT)
- err = uvwasi__translate_uv_error(r);
-
- uv_fs_req_cleanup(&req);
- goto exit;
- }
-
- /* Clean up memory and follow the link, unless it's time to return ELOOP. */
- follow_count++;
- if (follow_count >= UVWASI__MAX_SYMLINK_FOLLOWS) {
- uv_fs_req_cleanup(&req);
- err = UVWASI_ELOOP;
- goto exit;
- }
-
- input_len = strlen(req.ptr);
- uvwasi__free(uvwasi, link_target);
- link_target = uvwasi__malloc(uvwasi, input_len + 1);
- if (link_target == NULL) {
- uv_fs_req_cleanup(&req);
- err = UVWASI_ENOMEM;
- goto exit;
- }
-
- memcpy(link_target, req.ptr, input_len + 1);
- input = link_target;
- uvwasi__free(uvwasi, normalized_path);
- uv_fs_req_cleanup(&req);
- goto start;
- }
-
-exit:
- if (err == UVWASI_ESUCCESS)
- memcpy(resolved_path, host_path, host_path_len + 1);
-
- uvwasi__free(uvwasi, link_target);
- uvwasi__free(uvwasi, normalized_path);
- uvwasi__free(uvwasi, host_path);
- return err;
-}
-
-
static uvwasi_errno_t uvwasi__lseek(uv_file fd,
uvwasi_filedelta_t offset,
uvwasi_whence_t whence,
diff --git a/deps/uvwasi/uvwasi.gyp b/deps/uvwasi/uvwasi.gyp
index 6963cbf20a..42769095ec 100644
--- a/deps/uvwasi/uvwasi.gyp
+++ b/deps/uvwasi/uvwasi.gyp
@@ -11,6 +11,7 @@
'sources': [
'src/clocks.c',
'src/fd_table.c',
+ 'src/path_resolver.c',
'src/uv_mapping.c',
'src/uvwasi.c',
'src/wasi_rights.c',