Skip to content

Commit

Permalink
deps: uvwasi: cherry-pick c3bef8e
Browse files Browse the repository at this point in the history
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: nodejs/uvwasi#89
  • Loading branch information
cjihrig committed Jan 23, 2020
1 parent 6ec9a92 commit 0d0c835
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 69 deletions.
9 changes: 8 additions & 1 deletion deps/uvwasi/include/fd_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,24 @@ 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);
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__ */
67 changes: 49 additions & 18 deletions deps/uvwasi/src/fd_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}


Expand Down Expand Up @@ -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;
}
106 changes: 56 additions & 50 deletions deps/uvwasi/src/uvwasi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 0d0c835

Please sign in to comment.