From 849911dcc9469cf8213c364c919dac4e099f3111 Mon Sep 17 00:00:00 2001 From: Yage Hu Date: Tue, 16 Apr 2024 14:21:27 -0700 Subject: [PATCH] Prevent offset wrapping in `fd_pread` This commit inserts a bounds check in `fd_pread` for the `offset` parameter. This is necessary because an implicit unsigned-to-signed integer conversion is performed when `uv_fs_read` is called. Such a conversion results in implementation-defined behavior. One such behavior is the offset wrapping. fixes #257 --- src/uvwasi.c | 2 +- test/test-fd-pread-large-offset.c | 84 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 test/test-fd-pread-large-offset.c diff --git a/src/uvwasi.c b/src/uvwasi.c index 26efe2e..b535d67 100644 --- a/src/uvwasi.c +++ b/src/uvwasi.c @@ -1158,7 +1158,7 @@ uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, offset, nread); - if (uvwasi == NULL || iovs == NULL || nread == NULL) + if (uvwasi == NULL || iovs == NULL || nread == NULL || offset > INT64_MAX) return UVWASI_EINVAL; err = uvwasi_fd_table_get(uvwasi->fds, diff --git a/test/test-fd-pread-large-offset.c b/test/test-fd-pread-large-offset.c new file mode 100644 index 0000000..0d96f7f --- /dev/null +++ b/test/test-fd-pread-large-offset.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include "uvwasi.h" +#include "uv.h" +#include "test-common.h" + +#define TEST_TMP_DIR "./out/tmp" + +int main(void) { + const char* path = "file.txt"; + uvwasi_t uvwasi; + uvwasi_iovec_t* iovs; + uvwasi_fd_t fd; + uvwasi_filesize_t large_offset = (uint64_t) INT64_MAX + 1; + uvwasi_options_t init_options; + uvwasi_rights_t fs_rights_base; + uvwasi_size_t nread; + uvwasi_size_t iovs_len = 1; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + setup_test_environment(); + + r = uv_fs_mkdir(NULL, &req, TEST_TMP_DIR, 0777, NULL); + uv_fs_req_cleanup(&req); + assert(r == 0 || r == UV_EEXIST); + + uvwasi_options_init(&init_options); + init_options.preopenc = 1; + init_options.preopens = calloc(1, sizeof(uvwasi_preopen_t)); + init_options.preopens[0].mapped_path = "/var"; + init_options.preopens[0].real_path = TEST_TMP_DIR; + + err = uvwasi_init(&uvwasi, &init_options); + assert(err == 0); + + // Create a file. + fs_rights_base = UVWASI_RIGHT_FD_DATASYNC | + UVWASI_RIGHT_FD_FILESTAT_GET | + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | + UVWASI_RIGHT_FD_READ | + UVWASI_RIGHT_FD_SEEK | + UVWASI_RIGHT_FD_SYNC | + UVWASI_RIGHT_FD_TELL | + UVWASI_RIGHT_FD_WRITE | + UVWASI_RIGHT_PATH_UNLINK_FILE; + err = uvwasi_path_open(&uvwasi, + 3, + 1, + path, + strlen(path) + 1, + UVWASI_O_CREAT, + fs_rights_base, + 0, + 0, + &fd); + assert(err == 0); + + iovs = calloc(iovs_len, sizeof(uvwasi_iovec_t)); + assert(iovs != NULL); + + for (int i = 0; i < iovs_len; i++) { + iovs[i].buf_len = 1; + iovs[i].buf = malloc(1); + } + + err = uvwasi_fd_pread(&uvwasi, fd, iovs, iovs_len, large_offset, &nread); + assert(err == UVWASI_EINVAL); + + uvwasi_destroy(&uvwasi); + + for (int i = 0; i < iovs_len; i++) { + free((void *) iovs[i].buf); + } + + free(iovs); + free(init_options.preopens); + + return 0; +} +