diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h index a458cfe17daaa3..752c3ba3958e7c 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 19 +#define UVWASI_VERSION_PATCH 20 #define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \ (UVWASI_VERSION_MINOR << 8) | \ (UVWASI_VERSION_PATCH)) diff --git a/deps/uvwasi/src/path_resolver.c b/deps/uvwasi/src/path_resolver.c index af13c1553ca874..ec8946b1393d74 100644 --- a/deps/uvwasi/src/path_resolver.c +++ b/deps/uvwasi/src/path_resolver.c @@ -31,6 +31,42 @@ static char* uvwasi__strchr_slash(const char* s) { return NULL; } +static uvwasi_errno_t uvwasi__combine_paths(const uvwasi_t* uvwasi, + const char* path1, + uvwasi_size_t path1_len, + const char* path2, + uvwasi_size_t path2_len, + char** combined_path, + uvwasi_size_t* combined_len) { + /* This function joins two paths with '/'. */ + uvwasi_errno_t err; + char* combined; + int combined_size; + int r; + + *combined_path = NULL; + *combined_len = 0; + + /* The max combined size is the path1 length + the path2 length + + 2 for a terminating NULL and a possible path separator. */ + combined_size = path1_len + path2_len + 2; + combined = uvwasi__malloc(uvwasi, combined_size); + if (combined == NULL) return UVWASI_ENOMEM; + + r = snprintf(combined, combined_size, "%s/%s", path1, path2); + if (r <= 0) { + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + goto exit; + } + + err = UVWASI_ESUCCESS; + *combined_path = combined; + *combined_len = strlen(combined); + +exit: + if (err != UVWASI_ESUCCESS) uvwasi__free(uvwasi, combined); + return err; +} uvwasi_errno_t uvwasi__normalize_path(const char* path, uvwasi_size_t path_len, @@ -234,39 +270,35 @@ static uvwasi_errno_t uvwasi__normalize_relative_path( uvwasi_errno_t err; char* combined; char* normalized; - int combined_size; - int fd_path_len; - int norm_len; - int r; + uvwasi_size_t combined_len; + uvwasi_size_t fd_path_len; + uvwasi_size_t norm_len; *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); + err = uvwasi__combine_paths(uvwasi, + fd->normalized_path, + fd_path_len, + path, + path_len, + &combined, + &combined_len); + if (err != UVWASI_ESUCCESS) goto exit; + + normalized = uvwasi__malloc(uvwasi, combined_len + 1); 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, + combined_len, normalized, - combined_size - 1); + combined_len); if (err != UVWASI_ESUCCESS) goto exit; @@ -374,9 +406,14 @@ uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi, char* host_path; char* normalized_path; char* link_target; + char* normalized_parent; + char* resolved_link_target; uvwasi_size_t input_len; uvwasi_size_t host_path_len; uvwasi_size_t normalized_len; + uvwasi_size_t link_target_len; + uvwasi_size_t normalized_parent_len; + uvwasi_size_t resolved_link_target_len; int follow_count; int r; @@ -385,6 +422,8 @@ uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi, link_target = NULL; follow_count = 0; host_path = NULL; + normalized_parent = NULL; + resolved_link_target = NULL; start: normalized_path = NULL; @@ -458,19 +497,47 @@ uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi, goto exit; } - input_len = strlen(req.ptr); + link_target_len = strlen(req.ptr); uvwasi__free(uvwasi, link_target); - link_target = uvwasi__malloc(uvwasi, input_len + 1); + link_target = uvwasi__malloc(uvwasi, link_target_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); + memcpy(link_target, req.ptr, link_target_len + 1); uv_fs_req_cleanup(&req); + + if (1 == uvwasi__is_absolute_path(link_target, link_target_len)) { + input = link_target; + input_len = link_target_len; + } else { + uvwasi__free(uvwasi, normalized_parent); + uvwasi__free(uvwasi, resolved_link_target); + + err = uvwasi__combine_paths(uvwasi, + normalized_path, + normalized_len, + "..", + 2, + &normalized_parent, + &normalized_parent_len); + if (err != UVWASI_ESUCCESS) goto exit; + err = uvwasi__combine_paths(uvwasi, + normalized_parent, + normalized_parent_len, + link_target, + link_target_len, + &resolved_link_target, + &resolved_link_target_len); + if (err != UVWASI_ESUCCESS) goto exit; + + input = resolved_link_target; + input_len = resolved_link_target_len; + } + + uvwasi__free(uvwasi, normalized_path); goto start; } @@ -484,5 +551,8 @@ uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi, uvwasi__free(uvwasi, link_target); uvwasi__free(uvwasi, normalized_path); + uvwasi__free(uvwasi, normalized_parent); + uvwasi__free(uvwasi, resolved_link_target); + return err; } diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index e904b9f9293864..40162f886fa224 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -8,6 +8,8 @@ # include # include #else +# define _CRT_INTERNAL_NONSTDC_NAMES 1 +# include # include #endif /* _WIN32 */ @@ -17,6 +19,10 @@ # define UVWASI_FD_READDIR_SUPPORTED 1 #endif +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) + #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + #include "uvwasi.h" #include "uvwasi_alloc.h" #include "uv.h" @@ -37,10 +43,13 @@ #define VALIDATE_FSTFLAGS_OR_RETURN(flags) \ do { \ - if ((flags) & ~(UVWASI_FILESTAT_SET_ATIM | \ - UVWASI_FILESTAT_SET_ATIM_NOW | \ - UVWASI_FILESTAT_SET_MTIM | \ - UVWASI_FILESTAT_SET_MTIM_NOW)) { \ + uvwasi_fstflags_t f = flags; \ + if (((f) & ~(UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW | \ + UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) || \ + ((f) & (UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW)) \ + == (UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW) || \ + ((f) & (UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) \ + == (UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) { \ return UVWASI_EINVAL; \ } \ } while (0) @@ -624,9 +633,10 @@ uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, uvwasi_advice_t advice) { struct uvwasi_fd_wrap_t* wrap; uvwasi_errno_t err; + uv_fs_t req; + int r; #ifdef POSIX_FADV_NORMAL int mapped_advice; - int r; #endif /* POSIX_FADV_NORMAL */ UVWASI_DEBUG("uvwasi_fd_advise(uvwasi=%p, fd=%d, offset=%"PRIu64", " @@ -679,6 +689,17 @@ uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, if (err != UVWASI_ESUCCESS) return err; + r = uv_fs_fstat(NULL, &req, wrap->fd, NULL); + if (r == -1) { + err = uvwasi__translate_uv_error(r); + goto exit; + } + + if (S_ISDIR(req.statbuf.st_mode)) { + err = UVWASI_EBADF; + goto exit; + } + err = UVWASI_ESUCCESS; #ifdef POSIX_FADV_NORMAL @@ -686,7 +707,9 @@ uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, if (r != 0) err = uvwasi__translate_uv_error(uv_translate_sys_error(r)); #endif /* POSIX_FADV_NORMAL */ +exit: uv_mutex_unlock(&wrap->mutex); + uv_fs_req_cleanup(&req); return err; } @@ -1775,8 +1798,6 @@ uvwasi_errno_t uvwasi_path_filestat_set_times(uvwasi_t* uvwasi, if (uvwasi == NULL || path == NULL) return UVWASI_EINVAL; - VALIDATE_FSTFLAGS_OR_RETURN(fst_flags); - err = uvwasi_fd_table_get(uvwasi->fds, fd, &wrap, @@ -1785,6 +1806,8 @@ uvwasi_errno_t uvwasi_path_filestat_set_times(uvwasi_t* uvwasi, if (err != UVWASI_ESUCCESS) return err; + VALIDATE_FSTFLAGS_OR_RETURN(fst_flags); + err = uvwasi__resolve_path(uvwasi, wrap, path, @@ -2306,6 +2329,7 @@ uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, uvwasi_fd_t fd, const char* new_path, uvwasi_size_t new_path_len) { + char* truncated_old_path; char* resolved_new_path; struct uvwasi_fd_wrap_t* wrap; uvwasi_errno_t err; @@ -2332,6 +2356,15 @@ uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, if (err != UVWASI_ESUCCESS) return err; + truncated_old_path = uvwasi__malloc(uvwasi, old_path_len + 1); + if (truncated_old_path == NULL) { + uv_mutex_unlock(&wrap->mutex); + return UVWASI_ENOMEM; + } + + memcpy(truncated_old_path, old_path, old_path_len); + truncated_old_path[old_path_len] = '\0'; + err = uvwasi__resolve_path(uvwasi, wrap, new_path, @@ -2340,12 +2373,14 @@ uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, 0); if (err != UVWASI_ESUCCESS) { uv_mutex_unlock(&wrap->mutex); + uvwasi__free(uvwasi, truncated_old_path); return err; } /* Windows support may require setting the flags option. */ - r = uv_fs_symlink(NULL, &req, old_path, resolved_new_path, 0, NULL); + r = uv_fs_symlink(NULL, &req, truncated_old_path, resolved_new_path, 0, NULL); uv_mutex_unlock(&wrap->mutex); + uvwasi__free(uvwasi, truncated_old_path); uvwasi__free(uvwasi, resolved_new_path); uv_fs_req_cleanup(&req); if (r != 0) @@ -2696,7 +2731,7 @@ uvwasi_errno_t uvwasi_sock_shutdown(uvwasi_t* uvwasi, uvwasi_sdflags_t how) { struct uvwasi_fd_wrap_t* wrap; uvwasi_errno_t err = 0; - shutdown_data_t shutdown_data; + shutdown_data_t shutdown_data = {0}; if (how & ~UVWASI_SHUT_WR) return UVWASI_ENOTSUP; @@ -2794,7 +2829,7 @@ uvwasi_errno_t uvwasi_sock_accept(uvwasi_t* uvwasi, goto close_sock_and_error_exit; } - int r = uv_accept((uv_stream_t*) wrap->sock, (uv_stream_t*) uv_connect_sock); + r = uv_accept((uv_stream_t*) wrap->sock, (uv_stream_t*) uv_connect_sock); if (r == UV_EAGAIN) { // still no connection or error so run the loop again continue; diff --git a/test/wasi/c/create_symlink.c b/test/wasi/c/create_symlink.c index 094484ee7db745..319b10c6909781 100644 --- a/test/wasi/c/create_symlink.c +++ b/test/wasi/c/create_symlink.c @@ -4,7 +4,7 @@ #include int main() { - const char* target = "./input.txt"; + const char* target = "./input-in-subdir.txt"; const char* linkpath = "/sandbox/subdir/test_link"; char readlink_result[128]; size_t result_size = sizeof(readlink_result); diff --git a/test/wasi/test-wasi-symlinks.js b/test/wasi/test-wasi-symlinks.js index 9c95a0e55757d0..fb9fed65c2b480 100644 --- a/test/wasi/test-wasi-symlinks.js +++ b/test/wasi/test-wasi-symlinks.js @@ -42,6 +42,7 @@ if (process.argv[2] === 'wasi-child') { const sandboxedFile = path.join(sandbox, 'input.txt'); const externalFile = tmpdir.resolve('outside.txt'); const sandboxedDir = path.join(sandbox, 'subdir'); + const sandboxedFileInSubdir = path.join(sandboxedDir, 'input-in-subdir.txt'); const sandboxedSymlink = path.join(sandboxedDir, 'input_link.txt'); const escapingSymlink = path.join(sandboxedDir, 'outside.txt'); const loopSymlink1 = path.join(sandboxedDir, 'loop1'); @@ -52,13 +53,14 @@ if (process.argv[2] === 'wasi-child') { fs.mkdirSync(sandboxedDir); fs.mkdirSync(sandboxedTmp); fs.writeFileSync(sandboxedFile, 'hello from input.txt', 'utf8'); + fs.writeFileSync(sandboxedFileInSubdir, 'hello from input in subdir.txt', + 'utf8'); fs.writeFileSync(externalFile, 'this should be inaccessible', 'utf8'); - fs.symlinkSync(path.join('.', 'input.txt'), sandboxedSymlink, 'file'); - fs.symlinkSync(path.join('..', 'outside.txt'), escapingSymlink, 'file'); - fs.symlinkSync(path.join('subdir', 'loop2'), - loopSymlink1, 'file'); - fs.symlinkSync(path.join('subdir', 'loop1'), - loopSymlink2, 'file'); + fs.symlinkSync(path.join('.', 'input-in-subdir.txt'), + sandboxedSymlink, 'file'); + fs.symlinkSync(path.join('..', '..', 'outside.txt'), escapingSymlink, 'file'); + fs.symlinkSync('loop2', loopSymlink1, 'file'); + fs.symlinkSync('loop1', loopSymlink2, 'file'); function runWASI(options) { console.log('executing', options.test); @@ -76,8 +78,10 @@ if (process.argv[2] === 'wasi-child') { assert.strictEqual(child.stdout.toString(), options.stdout || ''); } - runWASI({ test: 'create_symlink', stdout: 'hello from input.txt' }); - runWASI({ test: 'follow_symlink', stdout: 'hello from input.txt' }); + runWASI({ test: 'create_symlink', + stdout: 'hello from input in subdir.txt' }); + runWASI({ test: 'follow_symlink', + stdout: 'hello from input in subdir.txt' }); runWASI({ test: 'link' }); runWASI({ test: 'symlink_escape' }); runWASI({ test: 'symlink_loop' }); diff --git a/test/wasi/wasm/create_symlink.wasm b/test/wasi/wasm/create_symlink.wasm index 89a24c50ca3146..64e120cd5a617b 100755 Binary files a/test/wasi/wasm/create_symlink.wasm and b/test/wasi/wasm/create_symlink.wasm differ