From 978f47783a1704acf8a64e31ce67c40e628bcd45 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Mon, 20 Jan 2020 18:25:40 -0500 Subject: [PATCH 1/5] deps: update to uvwasi 0.0.5 This version improves file descriptor renumbering, and as a result fixes uvwasi_fd_renumber(). --- deps/uvwasi/include/fd_table.h | 22 ++- deps/uvwasi/include/uvwasi.h | 2 +- deps/uvwasi/src/fd_table.c | 339 ++++++++++----------------------- deps/uvwasi/src/uv_mapping.c | 23 +++ deps/uvwasi/src/uv_mapping.h | 1 + deps/uvwasi/src/uvwasi.c | 80 +++----- deps/uvwasi/src/wasi_rights.c | 62 ++++++ deps/uvwasi/src/wasi_rights.h | 104 ++++++++++ deps/uvwasi/uvwasi.gyp | 1 + 9 files changed, 340 insertions(+), 294 deletions(-) create mode 100644 deps/uvwasi/src/wasi_rights.c create mode 100644 deps/uvwasi/src/wasi_rights.h diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h index 5380502a1e4e5f..23a3bf52f42c8b 100644 --- a/deps/uvwasi/include/fd_table.h +++ b/deps/uvwasi/include/fd_table.h @@ -31,19 +31,21 @@ uvwasi_errno_t uvwasi_fd_table_init(struct uvwasi_s* uvwasi, uint32_t init_size); void uvwasi_fd_table_free(struct uvwasi_s* uvwasi, struct uvwasi_fd_table_t* table); +uvwasi_errno_t uvwasi_fd_table_insert(struct uvwasi_s* uvwasi, + struct uvwasi_fd_table_t* table, + uv_file fd, + const char* mapped_path, + const char* real_path, + uvwasi_filetype_t type, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + int preopen, + struct uvwasi_fd_wrap_t** wrap); uvwasi_errno_t uvwasi_fd_table_insert_preopen(struct uvwasi_s* uvwasi, struct uvwasi_fd_table_t* table, const uv_file fd, const char* path, const char* real_path); -uvwasi_errno_t uvwasi_fd_table_insert_fd(struct uvwasi_s* uvwasi, - struct uvwasi_fd_table_t* table, - const uv_file fd, - const int flags, - const char* path, - uvwasi_rights_t rights_base, - uvwasi_rights_t rights_inheriting, - struct uvwasi_fd_wrap_t* wrap); uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, const uvwasi_fd_t id, struct uvwasi_fd_wrap_t** wrap, @@ -52,5 +54,9 @@ uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_s* uvwasi, struct uvwasi_fd_table_t* table, const uvwasi_fd_t id); +uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi, + struct uvwasi_fd_table_t* table, + const uvwasi_fd_t dst, + const uvwasi_fd_t src); #endif /* __UVWASI_FD_TABLE_H__ */ diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h index e32c335b101bf3..9ca30459e23a73 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 4 +#define UVWASI_VERSION_PATCH 5 #define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \ (UVWASI_VERSION_MINOR << 8) | \ (UVWASI_VERSION_PATCH)) diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c index f3855d4da54dbe..b6a43f0370e1e2 100644 --- a/deps/uvwasi/src/fd_table.c +++ b/deps/uvwasi/src/fd_table.c @@ -9,183 +9,21 @@ #include "uv.h" #include "fd_table.h" #include "wasi_types.h" +#include "wasi_rights.h" #include "uv_mapping.h" #include "uvwasi_alloc.h" -#define UVWASI__RIGHTS_ALL (UVWASI_RIGHT_FD_DATASYNC | \ - UVWASI_RIGHT_FD_READ | \ - UVWASI_RIGHT_FD_SEEK | \ - UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - UVWASI_RIGHT_FD_SYNC | \ - UVWASI_RIGHT_FD_TELL | \ - UVWASI_RIGHT_FD_WRITE | \ - UVWASI_RIGHT_FD_ADVISE | \ - UVWASI_RIGHT_FD_ALLOCATE | \ - UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ - UVWASI_RIGHT_PATH_CREATE_FILE | \ - UVWASI_RIGHT_PATH_LINK_SOURCE | \ - UVWASI_RIGHT_PATH_LINK_TARGET | \ - UVWASI_RIGHT_PATH_OPEN | \ - UVWASI_RIGHT_FD_READDIR | \ - UVWASI_RIGHT_PATH_READLINK | \ - UVWASI_RIGHT_PATH_RENAME_SOURCE | \ - UVWASI_RIGHT_PATH_RENAME_TARGET | \ - UVWASI_RIGHT_PATH_FILESTAT_GET | \ - UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ - UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ - UVWASI_RIGHT_FD_FILESTAT_GET | \ - UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ - UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ - UVWASI_RIGHT_PATH_SYMLINK | \ - UVWASI_RIGHT_PATH_UNLINK_FILE | \ - UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ - UVWASI_RIGHT_POLL_FD_READWRITE | \ - UVWASI_RIGHT_SOCK_SHUTDOWN) - -#define UVWASI__RIGHTS_BLOCK_DEVICE_BASE UVWASI__RIGHTS_ALL -#define UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING UVWASI__RIGHTS_ALL - -#define UVWASI__RIGHTS_CHARACTER_DEVICE_BASE UVWASI__RIGHTS_ALL -#define UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING UVWASI__RIGHTS_ALL - -#define UVWASI__RIGHTS_REGULAR_FILE_BASE (UVWASI_RIGHT_FD_DATASYNC | \ - UVWASI_RIGHT_FD_READ | \ - UVWASI_RIGHT_FD_SEEK | \ - UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - UVWASI_RIGHT_FD_SYNC | \ - UVWASI_RIGHT_FD_TELL | \ - UVWASI_RIGHT_FD_WRITE | \ - UVWASI_RIGHT_FD_ADVISE | \ - UVWASI_RIGHT_FD_ALLOCATE | \ - UVWASI_RIGHT_FD_FILESTAT_GET | \ - UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ - UVWASI_RIGHT_FD_FILESTAT_SET_TIMES |\ - UVWASI_RIGHT_POLL_FD_READWRITE) -#define UVWASI__RIGHTS_REGULAR_FILE_INHERITING 0 - -#define UVWASI__RIGHTS_DIRECTORY_BASE (UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - UVWASI_RIGHT_FD_SYNC | \ - UVWASI_RIGHT_FD_ADVISE | \ - UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ - UVWASI_RIGHT_PATH_CREATE_FILE | \ - UVWASI_RIGHT_PATH_LINK_SOURCE | \ - UVWASI_RIGHT_PATH_LINK_TARGET | \ - UVWASI_RIGHT_PATH_OPEN | \ - UVWASI_RIGHT_FD_READDIR | \ - UVWASI_RIGHT_PATH_READLINK | \ - UVWASI_RIGHT_PATH_RENAME_SOURCE | \ - UVWASI_RIGHT_PATH_RENAME_TARGET | \ - UVWASI_RIGHT_PATH_FILESTAT_GET | \ - UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ - UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ - UVWASI_RIGHT_FD_FILESTAT_GET | \ - UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ - UVWASI_RIGHT_PATH_SYMLINK | \ - UVWASI_RIGHT_PATH_UNLINK_FILE | \ - UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ - UVWASI_RIGHT_POLL_FD_READWRITE) -#define UVWASI__RIGHTS_DIRECTORY_INHERITING (UVWASI__RIGHTS_DIRECTORY_BASE | \ - UVWASI__RIGHTS_REGULAR_FILE_BASE) - -#define UVWASI__RIGHTS_SOCKET_BASE (UVWASI_RIGHT_FD_READ | \ - UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - UVWASI_RIGHT_FD_WRITE | \ - UVWASI_RIGHT_FD_FILESTAT_GET | \ - UVWASI_RIGHT_POLL_FD_READWRITE | \ - UVWASI_RIGHT_SOCK_SHUTDOWN) -#define UVWASI__RIGHTS_SOCKET_INHERITING UVWASI__RIGHTS_ALL; - -#define UVWASI__RIGHTS_TTY_BASE (UVWASI_RIGHT_FD_READ | \ - UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - UVWASI_RIGHT_FD_WRITE | \ - UVWASI_RIGHT_FD_FILESTAT_GET | \ - UVWASI_RIGHT_POLL_FD_READWRITE) -#define UVWASI__RIGHTS_TTY_INHERITING 0 - -static uvwasi_errno_t uvwasi__get_type_and_rights(uv_file fd, - int flags, - uvwasi_filetype_t* type, - uvwasi_rights_t* rights_base, - uvwasi_rights_t* rights_inheriting) { - uv_fs_t req; - uvwasi_filetype_t filetype; - int read_or_write_only; - int r; - - r = uv_fs_fstat(NULL, &req, fd, NULL); - filetype = uvwasi__stat_to_filetype(&req.statbuf); - uv_fs_req_cleanup(&req); - if (r != 0) - return uvwasi__translate_uv_error(r); - - *type = filetype; - switch (filetype) { - case UVWASI_FILETYPE_REGULAR_FILE: - *rights_base = UVWASI__RIGHTS_REGULAR_FILE_BASE; - *rights_inheriting = UVWASI__RIGHTS_REGULAR_FILE_INHERITING; - break; - - case UVWASI_FILETYPE_DIRECTORY: - *rights_base = UVWASI__RIGHTS_DIRECTORY_BASE; - *rights_inheriting = UVWASI__RIGHTS_DIRECTORY_INHERITING; - break; - - /* uvwasi__stat_to_filetype() cannot differentiate socket types. It only - returns UVWASI_FILETYPE_SOCKET_STREAM. */ - case UVWASI_FILETYPE_SOCKET_STREAM: - if (uv_guess_handle(fd) == UV_UDP) - *type = UVWASI_FILETYPE_SOCKET_DGRAM; - - *rights_base = UVWASI__RIGHTS_SOCKET_BASE; - *rights_inheriting = UVWASI__RIGHTS_SOCKET_INHERITING; - break; - - case UVWASI_FILETYPE_CHARACTER_DEVICE: - if (uv_guess_handle(fd) == UV_TTY) { - *rights_base = UVWASI__RIGHTS_TTY_BASE; - *rights_inheriting = UVWASI__RIGHTS_TTY_INHERITING; - } else { - *rights_base = UVWASI__RIGHTS_CHARACTER_DEVICE_BASE; - *rights_inheriting = UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING; - } - break; - - case UVWASI_FILETYPE_BLOCK_DEVICE: - *rights_base = UVWASI__RIGHTS_BLOCK_DEVICE_BASE; - *rights_inheriting = UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING; - break; - - default: - *rights_base = 0; - *rights_inheriting = 0; - } - - if (*type == UVWASI_FILETYPE_UNKNOWN) - return UVWASI_EINVAL; - - /* Disable read/write bits depending on access mode. */ - read_or_write_only = flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); - - if (read_or_write_only == UV_FS_O_RDONLY) - *rights_base &= ~UVWASI_RIGHT_FD_WRITE; - else if (read_or_write_only == UV_FS_O_WRONLY) - *rights_base &= ~UVWASI_RIGHT_FD_READ; - - return UVWASI_ESUCCESS; -} - - -static uvwasi_errno_t uvwasi__fd_table_insert(uvwasi_t* uvwasi, - struct uvwasi_fd_table_t* table, - uv_file fd, - const char* mapped_path, - const char* real_path, - uvwasi_filetype_t type, - uvwasi_rights_t rights_base, - uvwasi_rights_t rights_inheriting, - int preopen, - struct uvwasi_fd_wrap_t** wrap) { +uvwasi_errno_t uvwasi_fd_table_insert(uvwasi_t* uvwasi, + struct uvwasi_fd_table_t* table, + uv_file fd, + const char* mapped_path, + const char* real_path, + uvwasi_filetype_t type, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + int preopen, + struct uvwasi_fd_wrap_t** wrap) { struct uvwasi_fd_wrap_t* entry; struct uvwasi_fd_wrap_t** new_fds; uvwasi_errno_t err; @@ -263,11 +101,13 @@ static uvwasi_errno_t uvwasi__fd_table_insert(uvwasi_t* uvwasi, entry->rights_base = rights_base; entry->rights_inheriting = rights_inheriting; entry->preopen = preopen; - table->used++; - if (wrap != NULL) + if (wrap != NULL) { + uv_mutex_lock(&entry->mutex); *wrap = entry; + } + table->used++; err = UVWASI_ESUCCESS; exit: uv_rwlock_wrunlock(&table->rwlock); @@ -314,28 +154,30 @@ uvwasi_errno_t uvwasi_fd_table_init(uvwasi_t* uvwasi, /* Create the stdio FDs. */ for (i = 0; i < 3; ++i) { - err = uvwasi__get_type_and_rights(i, - UV_FS_O_RDWR, - &type, - &base, - &inheriting); + err = uvwasi__get_filetype_by_fd(i, &type); + if (err != UVWASI_ESUCCESS) + goto error_exit; + + err = uvwasi__get_rights(i, UV_FS_O_RDWR, type, &base, &inheriting); if (err != UVWASI_ESUCCESS) goto error_exit; - err = uvwasi__fd_table_insert(uvwasi, - table, - i, - "", - "", - type, - base, - inheriting, - 0, - &wrap); + err = uvwasi_fd_table_insert(uvwasi, + table, + i, + "", + "", + type, + base, + inheriting, + 0, + &wrap); if (err != UVWASI_ESUCCESS) goto error_exit; - if (wrap->id != i || wrap->id != (uvwasi_fd_t) wrap->fd) { + r = wrap->id != i || wrap->id != (uvwasi_fd_t) wrap->fd; + uv_mutex_unlock(&wrap->mutex); + if (r) { err = UVWASI_EBADF; goto error_exit; } @@ -386,14 +228,18 @@ uvwasi_errno_t uvwasi_fd_table_insert_preopen(uvwasi_t* uvwasi, if (table == NULL || path == NULL || real_path == NULL) return UVWASI_EINVAL; - err = uvwasi__get_type_and_rights(fd, 0, &type, &base, &inheriting); + err = uvwasi__get_filetype_by_fd(fd, &type); if (err != UVWASI_ESUCCESS) return err; if (type != UVWASI_FILETYPE_DIRECTORY) return UVWASI_ENOTDIR; - err = uvwasi__fd_table_insert(uvwasi, + err = uvwasi__get_rights(fd, 0, type, &base, &inheriting); + if (err != UVWASI_ESUCCESS) + return err; + + return uvwasi_fd_table_insert(uvwasi, table, fd, path, @@ -403,49 +249,6 @@ uvwasi_errno_t uvwasi_fd_table_insert_preopen(uvwasi_t* uvwasi, UVWASI__RIGHTS_DIRECTORY_INHERITING, 1, NULL); - if (err != UVWASI_ESUCCESS) - return err; - - return UVWASI_ESUCCESS; -} - - -uvwasi_errno_t uvwasi_fd_table_insert_fd(uvwasi_t* uvwasi, - struct uvwasi_fd_table_t* table, - const uv_file fd, - const int flags, - const char* path, - uvwasi_rights_t rights_base, - uvwasi_rights_t rights_inheriting, - struct uvwasi_fd_wrap_t* wrap) { - struct uvwasi_fd_wrap_t* fd_wrap; - uvwasi_filetype_t type; - uvwasi_rights_t max_base; - uvwasi_rights_t max_inheriting; - uvwasi_errno_t r; - - if (table == NULL || path == NULL || wrap == NULL) - return UVWASI_EINVAL; - - r = uvwasi__get_type_and_rights(fd, flags, &type, &max_base, &max_inheriting); - if (r != UVWASI_ESUCCESS) - return r; - - r = uvwasi__fd_table_insert(uvwasi, - table, - fd, - path, - path, - type, - rights_base & max_base, - rights_inheriting & max_inheriting, - 0, - &fd_wrap); - if (r != UVWASI_ESUCCESS) - return r; - - *wrap = *fd_wrap; - return UVWASI_ESUCCESS; } @@ -522,3 +325,67 @@ uvwasi_errno_t uvwasi_fd_table_remove(uvwasi_t* uvwasi, uv_rwlock_wrunlock(&table->rwlock); return err; } + + +uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi, + struct uvwasi_fd_table_t* table, + const uvwasi_fd_t dst, + const uvwasi_fd_t src) { + struct uvwasi_fd_wrap_t* dst_entry; + struct uvwasi_fd_wrap_t* src_entry; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || table == NULL) + return UVWASI_EINVAL; + + if (dst == src) + return UVWASI_ESUCCESS; + + uv_rwlock_wrlock(&table->rwlock); + + if (dst >= table->size || src >= table->size) { + err = UVWASI_EBADF; + goto exit; + } + + dst_entry = table->fds[dst]; + src_entry = table->fds[src]; + + if (dst_entry == NULL || dst_entry->id != dst || + src_entry == NULL || src_entry->id != src) { + err = UVWASI_EBADF; + goto exit; + } + + uv_mutex_lock(&dst_entry->mutex); + uv_mutex_lock(&src_entry->mutex); + + /* Close the existing destination descriptor. */ + r = uv_fs_close(NULL, &req, dst_entry->fd, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) { + uv_mutex_unlock(&dst_entry->mutex); + uv_mutex_unlock(&src_entry->mutex); + err = uvwasi__translate_uv_error(r); + goto exit; + } + + /* Move the source entry to the destination slot in the table. */ + table->fds[dst] = table->fds[src]; + table->fds[dst]->id = dst; + uv_mutex_unlock(&table->fds[dst]->mutex); + table->fds[src] = NULL; + table->used--; + + /* Clean up what's left of the old destination entry. */ + uv_mutex_unlock(&dst_entry->mutex); + uv_mutex_destroy(&dst_entry->mutex); + uvwasi__free(uvwasi, dst_entry); + + err = UVWASI_ESUCCESS; +exit: + uv_rwlock_wrunlock(&table->rwlock); + return err; +} diff --git a/deps/uvwasi/src/uv_mapping.c b/deps/uvwasi/src/uv_mapping.c index 846dcedbeb6b4f..297fdf5b75176f 100644 --- a/deps/uvwasi/src/uv_mapping.c +++ b/deps/uvwasi/src/uv_mapping.c @@ -241,3 +241,26 @@ void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs) { fs->st_mtim = uvwasi__timespec_to_timestamp(&stat->st_mtim); fs->st_ctim = uvwasi__timespec_to_timestamp(&stat->st_ctim); } + + +uvwasi_errno_t uvwasi__get_filetype_by_fd(uv_file fd, uvwasi_filetype_t* type) { + uv_fs_t req; + int r; + + r = uv_fs_fstat(NULL, &req, fd, NULL); + if (r != 0) { + *type = UVWASI_FILETYPE_UNKNOWN; + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + *type = uvwasi__stat_to_filetype(&req.statbuf); + uv_fs_req_cleanup(&req); + + if (*type == UVWASI_FILETYPE_SOCKET_STREAM && + uv_guess_handle(fd) == UV_UDP) { + *type = UVWASI_FILETYPE_SOCKET_DGRAM; + } + + return UVWASI_ESUCCESS; +} diff --git a/deps/uvwasi/src/uv_mapping.h b/deps/uvwasi/src/uv_mapping.h index d835ca507a4856..5a0542afb4dd8b 100644 --- a/deps/uvwasi/src/uv_mapping.h +++ b/deps/uvwasi/src/uv_mapping.h @@ -11,5 +11,6 @@ int uvwasi__translate_to_uv_signal(uvwasi_signal_t sig); uvwasi_timestamp_t uvwasi__timespec_to_timestamp(const uv_timespec_t* ts); uvwasi_filetype_t uvwasi__stat_to_filetype(const uv_stat_t* stat); void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs); +uvwasi_errno_t uvwasi__get_filetype_by_fd(uv_file fd, uvwasi_filetype_t* type); #endif /* __UVWASI_UV_MAPPING_H__ */ diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index e5d210657ce8b1..5a73bfee777e0a 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -22,6 +22,7 @@ #include "uv_mapping.h" #include "fd_table.h" #include "clocks.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. */ @@ -1485,41 +1486,10 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi, uvwasi_errno_t uvwasi_fd_renumber(uvwasi_t* uvwasi, uvwasi_fd_t from, uvwasi_fd_t to) { - struct uvwasi_fd_wrap_t* to_wrap; - struct uvwasi_fd_wrap_t* from_wrap; - uv_fs_t req; - uvwasi_errno_t err; - int r; - if (uvwasi == NULL) return UVWASI_EINVAL; - if (from == to) - return UVWASI_ESUCCESS; - - err = uvwasi_fd_table_get(&uvwasi->fds, from, &from_wrap, 0, 0); - if (err != UVWASI_ESUCCESS) - return err; - - err = uvwasi_fd_table_get(&uvwasi->fds, to, &to_wrap, 0, 0); - if (err != UVWASI_ESUCCESS) { - uv_mutex_unlock(&from_wrap->mutex); - return err; - } - - r = uv_fs_close(NULL, &req, to_wrap->fd, NULL); - uv_fs_req_cleanup(&req); - if (r != 0) { - uv_mutex_unlock(&from_wrap->mutex); - uv_mutex_unlock(&to_wrap->mutex); - return uvwasi__translate_uv_error(r); - } - - memcpy(to_wrap, from_wrap, sizeof(*to_wrap)); - to_wrap->id = to; - uv_mutex_unlock(&from_wrap->mutex); - uv_mutex_unlock(&to_wrap->mutex); - return uvwasi_fd_table_remove(uvwasi, &uvwasi->fds, from); + return uvwasi_fd_table_renumber(uvwasi, &uvwasi->fds, to, from); } @@ -1871,8 +1841,11 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, char resolved_path[PATH_MAX_BYTES]; uvwasi_rights_t needed_inheriting; uvwasi_rights_t needed_base; + uvwasi_rights_t max_base; + uvwasi_rights_t max_inheriting; struct uvwasi_fd_wrap_t* dirfd_wrap; - struct uvwasi_fd_wrap_t wrap; + struct uvwasi_fd_wrap_t *wrap; + uvwasi_filetype_t filetype; uvwasi_errno_t err; uv_fs_t req; int flags; @@ -1956,33 +1929,42 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, return uvwasi__translate_uv_error(r); } - err = uvwasi_fd_table_insert_fd(uvwasi, - &uvwasi->fds, - r, - flags, - resolved_path, - fs_rights_base, - fs_rights_inheriting, - &wrap); - if (err != UVWASI_ESUCCESS) { - uv_mutex_unlock(&dirfd_wrap->mutex); + /* Not all platforms support UV_FS_O_DIRECTORY, so get the file type and check + it here. */ + err = uvwasi__get_filetype_by_fd(r, &filetype); + if (err != UVWASI_ESUCCESS) goto close_file_and_error_exit; - } - /* Not all platforms support UV_FS_O_DIRECTORY, so enforce it here as well. */ if ((o_flags & UVWASI_O_DIRECTORY) != 0 && - wrap.type != UVWASI_FILETYPE_DIRECTORY) { - uv_mutex_unlock(&dirfd_wrap->mutex); - uvwasi_fd_table_remove(uvwasi, &uvwasi->fds, wrap.id); + filetype != UVWASI_FILETYPE_DIRECTORY) { err = UVWASI_ENOTDIR; goto close_file_and_error_exit; } - *fd = wrap.id; + err = uvwasi__get_rights(r, flags, filetype, &max_base, &max_inheriting); + if (err != UVWASI_ESUCCESS) + goto close_file_and_error_exit; + + err = uvwasi_fd_table_insert(uvwasi, + &uvwasi->fds, + r, + resolved_path, + resolved_path, + filetype, + fs_rights_base & max_base, + fs_rights_inheriting & max_inheriting, + 0, + &wrap); + if (err != UVWASI_ESUCCESS) + goto close_file_and_error_exit; + + *fd = wrap->id; + uv_mutex_unlock(&wrap->mutex); uv_mutex_unlock(&dirfd_wrap->mutex); return UVWASI_ESUCCESS; close_file_and_error_exit: + uv_mutex_unlock(&dirfd_wrap->mutex); uv_fs_close(NULL, &req, r, NULL); uv_fs_req_cleanup(&req); return err; diff --git a/deps/uvwasi/src/wasi_rights.c b/deps/uvwasi/src/wasi_rights.c new file mode 100644 index 00000000000000..79d0338086a244 --- /dev/null +++ b/deps/uvwasi/src/wasi_rights.c @@ -0,0 +1,62 @@ +#include "uv.h" +#include "wasi_rights.h" +#include "wasi_types.h" + + +uvwasi_errno_t uvwasi__get_rights(uv_file fd, + int flags, + uvwasi_filetype_t type, + uvwasi_rights_t* rights_base, + uvwasi_rights_t* rights_inheriting) { + int read_or_write_only; + + if (type == UVWASI_FILETYPE_UNKNOWN) + return UVWASI_EINVAL; + + switch (type) { + case UVWASI_FILETYPE_REGULAR_FILE: + *rights_base = UVWASI__RIGHTS_REGULAR_FILE_BASE; + *rights_inheriting = UVWASI__RIGHTS_REGULAR_FILE_INHERITING; + break; + + case UVWASI_FILETYPE_DIRECTORY: + *rights_base = UVWASI__RIGHTS_DIRECTORY_BASE; + *rights_inheriting = UVWASI__RIGHTS_DIRECTORY_INHERITING; + break; + + case UVWASI_FILETYPE_SOCKET_STREAM: + case UVWASI_FILETYPE_SOCKET_DGRAM: + *rights_base = UVWASI__RIGHTS_SOCKET_BASE; + *rights_inheriting = UVWASI__RIGHTS_SOCKET_INHERITING; + break; + + case UVWASI_FILETYPE_CHARACTER_DEVICE: + if (uv_guess_handle(fd) == UV_TTY) { + *rights_base = UVWASI__RIGHTS_TTY_BASE; + *rights_inheriting = UVWASI__RIGHTS_TTY_INHERITING; + } else { + *rights_base = UVWASI__RIGHTS_CHARACTER_DEVICE_BASE; + *rights_inheriting = UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING; + } + break; + + case UVWASI_FILETYPE_BLOCK_DEVICE: + *rights_base = UVWASI__RIGHTS_BLOCK_DEVICE_BASE; + *rights_inheriting = UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING; + break; + + default: + *rights_base = 0; + *rights_inheriting = 0; + } + + /* Disable read/write bits depending on access mode. */ + read_or_write_only = flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + + if (read_or_write_only == UV_FS_O_RDONLY) + *rights_base &= ~UVWASI_RIGHT_FD_WRITE; + else if (read_or_write_only == UV_FS_O_WRONLY) + *rights_base &= ~UVWASI_RIGHT_FD_READ; + + return UVWASI_ESUCCESS; +} diff --git a/deps/uvwasi/src/wasi_rights.h b/deps/uvwasi/src/wasi_rights.h new file mode 100644 index 00000000000000..fb19bd0a00e74e --- /dev/null +++ b/deps/uvwasi/src/wasi_rights.h @@ -0,0 +1,104 @@ +#ifndef __UVWASI_WASI_RIGHTS_H__ +#define __UVWASI_WASI_RIGHTS_H__ + +#include "wasi_types.h" + +#define UVWASI__RIGHTS_ALL (UVWASI_RIGHT_FD_DATASYNC | \ + UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_SEEK | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_TELL | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_FD_ALLOCATE | \ + UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ + UVWASI_RIGHT_PATH_CREATE_FILE | \ + UVWASI_RIGHT_PATH_LINK_SOURCE | \ + UVWASI_RIGHT_PATH_LINK_TARGET | \ + UVWASI_RIGHT_PATH_OPEN | \ + UVWASI_RIGHT_FD_READDIR | \ + UVWASI_RIGHT_PATH_READLINK | \ + UVWASI_RIGHT_PATH_RENAME_SOURCE | \ + UVWASI_RIGHT_PATH_RENAME_TARGET | \ + UVWASI_RIGHT_PATH_FILESTAT_GET | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_SYMLINK | \ + UVWASI_RIGHT_PATH_UNLINK_FILE | \ + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + UVWASI_RIGHT_POLL_FD_READWRITE | \ + UVWASI_RIGHT_SOCK_SHUTDOWN) + +#define UVWASI__RIGHTS_BLOCK_DEVICE_BASE UVWASI__RIGHTS_ALL +#define UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING UVWASI__RIGHTS_ALL + +#define UVWASI__RIGHTS_CHARACTER_DEVICE_BASE UVWASI__RIGHTS_ALL +#define UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING UVWASI__RIGHTS_ALL + +#define UVWASI__RIGHTS_REGULAR_FILE_BASE (UVWASI_RIGHT_FD_DATASYNC | \ + UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_SEEK | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_TELL | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_FD_ALLOCATE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES |\ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_REGULAR_FILE_INHERITING 0 + +#define UVWASI__RIGHTS_DIRECTORY_BASE (UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ + UVWASI_RIGHT_PATH_CREATE_FILE | \ + UVWASI_RIGHT_PATH_LINK_SOURCE | \ + UVWASI_RIGHT_PATH_LINK_TARGET | \ + UVWASI_RIGHT_PATH_OPEN | \ + UVWASI_RIGHT_FD_READDIR | \ + UVWASI_RIGHT_PATH_READLINK | \ + UVWASI_RIGHT_PATH_RENAME_SOURCE | \ + UVWASI_RIGHT_PATH_RENAME_TARGET | \ + UVWASI_RIGHT_PATH_FILESTAT_GET | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_PATH_SYMLINK | \ + UVWASI_RIGHT_PATH_UNLINK_FILE | \ + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_DIRECTORY_INHERITING (UVWASI__RIGHTS_DIRECTORY_BASE | \ + UVWASI__RIGHTS_REGULAR_FILE_BASE) + +#define UVWASI__RIGHTS_SOCKET_BASE (UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_POLL_FD_READWRITE | \ + UVWASI_RIGHT_SOCK_SHUTDOWN) +#define UVWASI__RIGHTS_SOCKET_INHERITING UVWASI__RIGHTS_ALL; + +#define UVWASI__RIGHTS_TTY_BASE (UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_TTY_INHERITING 0 + + +uvwasi_errno_t uvwasi__get_rights(uv_file fd, + int flags, + uvwasi_filetype_t type, + uvwasi_rights_t* rights_base, + uvwasi_rights_t* rights_inheriting); + + +#endif /* __UVWASI_WASI_RIGHTS_H__ */ diff --git a/deps/uvwasi/uvwasi.gyp b/deps/uvwasi/uvwasi.gyp index 21ac35cdfc11ad..6963cbf20a7923 100644 --- a/deps/uvwasi/uvwasi.gyp +++ b/deps/uvwasi/uvwasi.gyp @@ -13,6 +13,7 @@ 'src/fd_table.c', 'src/uv_mapping.c', 'src/uvwasi.c', + 'src/wasi_rights.c', ], 'dependencies': [ '../uv/uv.gyp:libuv', From 6ec9a92ef0e191257d755a19a5ec34b7e7418869 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 21 Jan 2020 09:29:36 -0500 Subject: [PATCH 2/5] deps: uvwasi: cherry-pick ea73af5 Original commit message: unlock all fd mutexes in reverse order Some functions acquire mutexes for multiple file descriptors. This commit ensures that the mutexes are released in the reverse order that they are aquired. --- deps/uvwasi/src/fd_table.c | 2 +- deps/uvwasi/src/uvwasi.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c index b6a43f0370e1e2..e4897769d987e1 100644 --- a/deps/uvwasi/src/fd_table.c +++ b/deps/uvwasi/src/fd_table.c @@ -366,8 +366,8 @@ uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi, r = uv_fs_close(NULL, &req, dst_entry->fd, NULL); uv_fs_req_cleanup(&req); if (r != 0) { - uv_mutex_unlock(&dst_entry->mutex); uv_mutex_unlock(&src_entry->mutex); + uv_mutex_unlock(&dst_entry->mutex); err = uvwasi__translate_uv_error(r); goto exit; } diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index 5a73bfee777e0a..e4c2ac6e3b0dd1 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -1821,9 +1821,9 @@ uvwasi_errno_t uvwasi_path_link(uvwasi_t* uvwasi, err = UVWASI_ESUCCESS; exit: - uv_mutex_unlock(&old_wrap->mutex); + uv_mutex_unlock(&new_wrap->mutex); if (old_fd != new_fd) - uv_mutex_unlock(&new_wrap->mutex); + uv_mutex_unlock(&old_wrap->mutex); return err; } @@ -2136,9 +2136,9 @@ uvwasi_errno_t uvwasi_path_rename(uvwasi_t* uvwasi, err = UVWASI_ESUCCESS; exit: - uv_mutex_unlock(&old_wrap->mutex); + uv_mutex_unlock(&new_wrap->mutex); if (old_fd != new_fd) - uv_mutex_unlock(&new_wrap->mutex); + uv_mutex_unlock(&old_wrap->mutex); return err; } From 0d0c83524e7e2fb32ef506d677d565c4eca1daae Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 21 Jan 2020 22:33:24 -0500 Subject: [PATCH 3/5] deps: uvwasi: cherry-pick c3bef8e Original commit message: prevent locking fd table while holding a mutex uvwasi_path_rename(), uvwasi_path_link(), uvwasi_path_open(), and uvwasi_fd_renumber() operate on multiple file descriptors. uvwasi_fd_renumber() has been updated prior to this commit, and is not relevant here. The other three functions would perform the following locking operations: - lock the file table - acquire a file descriptor mutex - unlock the file table - unlock the file table again - acquire another file descriptor mutex - unlock the file table - unlock the two mutexes Attempting to acquire the second mutex introduced the possibility of deadlock because another thread could attempt to acquire the first mutex while holding the file table lock. This commit ensures that multiple mutexes are either: - acquired in a single lock of the file table - or, only acquired after releasing previously held mutexes Fixes: https://github.com/cjihrig/uvwasi/issues/89 --- deps/uvwasi/include/fd_table.h | 9 ++- deps/uvwasi/src/fd_table.c | 67 +++++++++++++++------ deps/uvwasi/src/uvwasi.c | 106 +++++++++++++++++---------------- 3 files changed, 113 insertions(+), 69 deletions(-) diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h index 23a3bf52f42c8b..9d88628e22c62d 100644 --- a/deps/uvwasi/include/fd_table.h +++ b/deps/uvwasi/include/fd_table.h @@ -46,11 +46,16 @@ uvwasi_errno_t uvwasi_fd_table_insert_preopen(struct uvwasi_s* uvwasi, const uv_file fd, const char* path, const char* real_path); -uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, +uvwasi_errno_t uvwasi_fd_table_get(struct uvwasi_fd_table_t* table, const uvwasi_fd_t id, struct uvwasi_fd_wrap_t** wrap, uvwasi_rights_t rights_base, uvwasi_rights_t rights_inheriting); +uvwasi_errno_t uvwasi_fd_table_get_nolock(struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id, + struct uvwasi_fd_wrap_t** wrap, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting); uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_s* uvwasi, struct uvwasi_fd_table_t* table, const uvwasi_fd_t id); @@ -58,5 +63,7 @@ uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi, struct uvwasi_fd_table_t* table, const uvwasi_fd_t dst, const uvwasi_fd_t src); +uvwasi_errno_t uvwasi_fd_table_lock(struct uvwasi_fd_table_t* table); +uvwasi_errno_t uvwasi_fd_table_unlock(struct uvwasi_fd_table_t* table); #endif /* __UVWASI_FD_TABLE_H__ */ diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c index e4897769d987e1..c15ea09257506c 100644 --- a/deps/uvwasi/src/fd_table.c +++ b/deps/uvwasi/src/fd_table.c @@ -252,44 +252,57 @@ uvwasi_errno_t uvwasi_fd_table_insert_preopen(uvwasi_t* uvwasi, } -uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, +uvwasi_errno_t uvwasi_fd_table_get(struct uvwasi_fd_table_t* table, const uvwasi_fd_t id, struct uvwasi_fd_wrap_t** wrap, uvwasi_rights_t rights_base, uvwasi_rights_t rights_inheriting) { - struct uvwasi_fd_wrap_t* entry; uvwasi_errno_t err; - if (table == NULL || wrap == NULL) + if (table == NULL) return UVWASI_EINVAL; - uv_rwlock_rdlock((uv_rwlock_t *)&table->rwlock); + uv_rwlock_wrlock(&table->rwlock); + err = uvwasi_fd_table_get_nolock(table, + id, + wrap, + rights_base, + rights_inheriting); + uv_rwlock_wrunlock(&table->rwlock); + return err; +} - if (id >= table->size) { - err = UVWASI_EBADF; - goto exit; - } + +/* uvwasi_fd_table_get_nolock() retrieves a file descriptor and locks its mutex, + but does not lock the file descriptor table like uvwasi_fd_table_get() does. +*/ +uvwasi_errno_t uvwasi_fd_table_get_nolock(struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id, + struct uvwasi_fd_wrap_t** wrap, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting) { + struct uvwasi_fd_wrap_t* entry; + + if (table == NULL || wrap == NULL) + return UVWASI_EINVAL; + + if (id >= table->size) + return UVWASI_EBADF; entry = table->fds[id]; - if (entry == NULL || entry->id != id) { - err = UVWASI_EBADF; - goto exit; - } + if (entry == NULL || entry->id != id) + return UVWASI_EBADF; /* Validate that the fd has the necessary rights. */ if ((~entry->rights_base & rights_base) != 0 || (~entry->rights_inheriting & rights_inheriting) != 0) { - err = UVWASI_ENOTCAPABLE; - goto exit; + return UVWASI_ENOTCAPABLE; } uv_mutex_lock(&entry->mutex); *wrap = entry; - err = UVWASI_ESUCCESS; -exit: - uv_rwlock_rdunlock((uv_rwlock_t *)&table->rwlock); - return err; + return UVWASI_ESUCCESS; } @@ -389,3 +402,21 @@ uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi, uv_rwlock_wrunlock(&table->rwlock); return err; } + + +uvwasi_errno_t uvwasi_fd_table_lock(struct uvwasi_fd_table_t* table) { + if (table == NULL) + return UVWASI_EINVAL; + + uv_rwlock_wrlock(&table->rwlock); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_unlock(struct uvwasi_fd_table_t* table) { + if (table == NULL) + return UVWASI_EINVAL; + + uv_rwlock_wrunlock(&table->rwlock); + return UVWASI_ESUCCESS; +} diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index e4c2ac6e3b0dd1..9fa4db8a521111 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -1763,37 +1763,41 @@ uvwasi_errno_t uvwasi_path_link(uvwasi_t* uvwasi, if (uvwasi == NULL || old_path == NULL || new_path == NULL) return UVWASI_EINVAL; - if (old_fd == new_fd) { - err = uvwasi_fd_table_get(&uvwasi->fds, - old_fd, - &old_wrap, - UVWASI_RIGHT_PATH_LINK_SOURCE | - UVWASI_RIGHT_PATH_LINK_TARGET, - 0); - if (err != UVWASI_ESUCCESS) - return err; + uvwasi_fd_table_lock(&uvwasi->fds); + if (old_fd == new_fd) { + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_LINK_SOURCE | + UVWASI_RIGHT_PATH_LINK_TARGET, + 0); new_wrap = old_wrap; } else { - err = uvwasi_fd_table_get(&uvwasi->fds, - old_fd, - &old_wrap, - UVWASI_RIGHT_PATH_LINK_SOURCE, - 0); - if (err != UVWASI_ESUCCESS) - return err; - - err = uvwasi_fd_table_get(&uvwasi->fds, - new_fd, - &new_wrap, - UVWASI_RIGHT_PATH_LINK_TARGET, - 0); + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_LINK_SOURCE, + 0); if (err != UVWASI_ESUCCESS) { - uv_mutex_unlock(&old_wrap->mutex); + uvwasi_fd_table_unlock(&uvwasi->fds); return err; } + + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, + new_fd, + &new_wrap, + UVWASI_RIGHT_PATH_LINK_TARGET, + 0); + if (err != UVWASI_ESUCCESS) + uv_mutex_unlock(&old_wrap->mutex); } + uvwasi_fd_table_unlock(&uvwasi->fds); + + if (err != UVWASI_ESUCCESS) + return err; + err = uvwasi__resolve_path(uvwasi, old_wrap, old_path, @@ -1922,12 +1926,11 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, } r = uv_fs_open(NULL, &req, resolved_path, flags, 0666, NULL); + uv_mutex_unlock(&dirfd_wrap->mutex); uv_fs_req_cleanup(&req); - if (r < 0) { - uv_mutex_unlock(&dirfd_wrap->mutex); + if (r < 0) return uvwasi__translate_uv_error(r); - } /* Not all platforms support UV_FS_O_DIRECTORY, so get the file type and check it here. */ @@ -1960,11 +1963,9 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, *fd = wrap->id; uv_mutex_unlock(&wrap->mutex); - uv_mutex_unlock(&dirfd_wrap->mutex); return UVWASI_ESUCCESS; close_file_and_error_exit: - uv_mutex_unlock(&dirfd_wrap->mutex); uv_fs_close(NULL, &req, r, NULL); uv_fs_req_cleanup(&req); return err; @@ -2079,36 +2080,41 @@ uvwasi_errno_t uvwasi_path_rename(uvwasi_t* uvwasi, if (uvwasi == NULL || old_path == NULL || new_path == NULL) return UVWASI_EINVAL; + uvwasi_fd_table_lock(&uvwasi->fds); + if (old_fd == new_fd) { - err = uvwasi_fd_table_get(&uvwasi->fds, - old_fd, - &old_wrap, - UVWASI_RIGHT_PATH_RENAME_SOURCE | - UVWASI_RIGHT_PATH_RENAME_TARGET, - 0); - if (err != UVWASI_ESUCCESS) - return err; + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_RENAME_SOURCE | + UVWASI_RIGHT_PATH_RENAME_TARGET, + 0); new_wrap = old_wrap; } else { - err = uvwasi_fd_table_get(&uvwasi->fds, - old_fd, - &old_wrap, - UVWASI_RIGHT_PATH_RENAME_SOURCE, - 0); - if (err != UVWASI_ESUCCESS) - return err; - - err = uvwasi_fd_table_get(&uvwasi->fds, - new_fd, - &new_wrap, - UVWASI_RIGHT_PATH_RENAME_TARGET, - 0); + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_RENAME_SOURCE, + 0); if (err != UVWASI_ESUCCESS) { - uv_mutex_unlock(&old_wrap->mutex); + uvwasi_fd_table_unlock(&uvwasi->fds); return err; } + + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, + new_fd, + &new_wrap, + UVWASI_RIGHT_PATH_RENAME_TARGET, + 0); + if (err != UVWASI_ESUCCESS) + uv_mutex_unlock(&old_wrap->mutex); } + uvwasi_fd_table_unlock(&uvwasi->fds); + + if (err != UVWASI_ESUCCESS) + return err; + err = uvwasi__resolve_path(uvwasi, old_wrap, old_path, From 2fe7459e5b470cc8cc8c7a5e202afa850036ddf0 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 21 Jan 2020 23:08:38 -0500 Subject: [PATCH 4/5] deps: uvwasi: cherry-pick eea4508 Original commit message: prevent race conditions with uvwasi_fd_close() uvwasi_fd_close() performed the following operations: - lock the file descriptor mutex - close the file - release the file descriptor mutex - call the file table's remove() function Once the fd's mutex is released, another thread could acquire it before the fd is removed from the file table. If this happens, remove() could destroy a held mutex. This commit updates uvwasi_fd_close() to perform the entire sequence while holding the file table's lock, preventing new acquisitions of the fd's mutex. Fixes: https://github.com/cjihrig/uvwasi/issues/88 --- deps/uvwasi/include/fd_table.h | 6 +++--- deps/uvwasi/src/fd_table.c | 26 ++++++++------------------ deps/uvwasi/src/uvwasi.c | 18 +++++++++++++----- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h index 9d88628e22c62d..fa8a44e7468d41 100644 --- a/deps/uvwasi/include/fd_table.h +++ b/deps/uvwasi/include/fd_table.h @@ -56,9 +56,9 @@ uvwasi_errno_t uvwasi_fd_table_get_nolock(struct uvwasi_fd_table_t* table, struct uvwasi_fd_wrap_t** wrap, uvwasi_rights_t rights_base, uvwasi_rights_t rights_inheriting); -uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_s* uvwasi, - struct uvwasi_fd_table_t* table, - const uvwasi_fd_t id); +uvwasi_errno_t uvwasi_fd_table_remove_nolock(struct uvwasi_s* uvwasi, + struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id); uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi, struct uvwasi_fd_table_t* table, const uvwasi_fd_t dst, diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c index c15ea09257506c..bc32f4dd28bbce 100644 --- a/deps/uvwasi/src/fd_table.c +++ b/deps/uvwasi/src/fd_table.c @@ -306,37 +306,27 @@ uvwasi_errno_t uvwasi_fd_table_get_nolock(struct uvwasi_fd_table_t* table, } -uvwasi_errno_t uvwasi_fd_table_remove(uvwasi_t* uvwasi, - struct uvwasi_fd_table_t* table, - const uvwasi_fd_t id) { +uvwasi_errno_t uvwasi_fd_table_remove_nolock(uvwasi_t* uvwasi, + struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id) { struct uvwasi_fd_wrap_t* entry; - uvwasi_errno_t err; if (table == NULL) return UVWASI_EINVAL; - uv_rwlock_wrlock(&table->rwlock); - - if (id >= table->size) { - err = UVWASI_EBADF; - goto exit; - } + if (id >= table->size) + return UVWASI_EBADF; entry = table->fds[id]; - if (entry == NULL || entry->id != id) { - err = UVWASI_EBADF; - goto exit; - } + if (entry == NULL || entry->id != id) + return UVWASI_EBADF; uv_mutex_destroy(&entry->mutex); uvwasi__free(uvwasi, entry); table->fds[id] = NULL; table->used--; - err = UVWASI_ESUCCESS; -exit: - uv_rwlock_wrunlock(&table->rwlock); - return err; + return UVWASI_ESUCCESS; } diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index 9fa4db8a521111..53b7699f590e53 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -878,18 +878,26 @@ uvwasi_errno_t uvwasi_fd_close(uvwasi_t* uvwasi, uvwasi_fd_t fd) { if (uvwasi == NULL) return UVWASI_EINVAL; - err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + uvwasi_fd_table_lock(&uvwasi->fds); + + err = uvwasi_fd_table_get_nolock(&uvwasi->fds, fd, &wrap, 0, 0); if (err != UVWASI_ESUCCESS) - return err; + goto exit; r = uv_fs_close(NULL, &req, wrap->fd, NULL); uv_mutex_unlock(&wrap->mutex); uv_fs_req_cleanup(&req); - if (r != 0) - return uvwasi__translate_uv_error(r); + if (r != 0) { + err = uvwasi__translate_uv_error(r); + goto exit; + } + + err = uvwasi_fd_table_remove_nolock(uvwasi, &uvwasi->fds, fd); - return uvwasi_fd_table_remove(uvwasi, &uvwasi->fds, fd); +exit: + uvwasi_fd_table_unlock(&uvwasi->fds); + return err; } From 1dc6d2f0c44ea1f93ebc1306632e92719726004d Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sat, 18 Jan 2020 12:52:04 -0500 Subject: [PATCH 5/5] test: add wasi test for freopen() This test provides missing coverage for __wasi_fd_renumber(). --- test/fixtures/wasi/input2.txt | 1 + test/wasi/c/freopen.c | 16 ++++++++++++++++ test/wasi/test-wasi.js | 1 + test/wasi/wasm/freopen.wasm | Bin 0 -> 35202 bytes 4 files changed, 18 insertions(+) create mode 100644 test/fixtures/wasi/input2.txt create mode 100644 test/wasi/c/freopen.c create mode 100755 test/wasi/wasm/freopen.wasm diff --git a/test/fixtures/wasi/input2.txt b/test/fixtures/wasi/input2.txt new file mode 100644 index 00000000000000..6aadea38006fb0 --- /dev/null +++ b/test/fixtures/wasi/input2.txt @@ -0,0 +1 @@ +hello from input2.txt diff --git a/test/wasi/c/freopen.c b/test/wasi/c/freopen.c new file mode 100644 index 00000000000000..0f47058da2ca95 --- /dev/null +++ b/test/wasi/c/freopen.c @@ -0,0 +1,16 @@ +#include +#include + +int main() { + FILE* file_orig = fopen("/sandbox/input.txt", "r"); + assert(file_orig != NULL); + FILE* file_new = freopen("/sandbox/input2.txt", "r", file_orig); + assert(file_new != NULL); + + int c = fgetc(file_new); + while (c != EOF) { + int wrote = fputc((char)c, stdout); + assert(wrote != EOF); + c = fgetc(file_new); + } +} diff --git a/test/wasi/test-wasi.js b/test/wasi/test-wasi.js index 9c8b2a752778f9..01d5aa8fe5cc28 100644 --- a/test/wasi/test-wasi.js +++ b/test/wasi/test-wasi.js @@ -60,6 +60,7 @@ if (process.argv[2] === 'wasi-child') { runWASI({ test: 'clock_getres' }); runWASI({ test: 'exitcode', exitCode: 120 }); runWASI({ test: 'fd_prestat_get_refresh' }); + runWASI({ test: 'freopen', stdout: `hello from input2.txt${EOL}` }); runWASI({ test: 'getentropy' }); runWASI({ test: 'getrusage' }); runWASI({ test: 'gettimeofday' }); diff --git a/test/wasi/wasm/freopen.wasm b/test/wasi/wasm/freopen.wasm new file mode 100755 index 0000000000000000000000000000000000000000..fb417fbe21fa69299a9892d8d4bf41047429eecc GIT binary patch literal 35202 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 P=`~)p)8GbYDmVN;g9@%i literal 0 HcmV?d00001