diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d6c4e96d8..1099066b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,7 +39,7 @@ jobs: upload: windows-clang-20 - name: Build on WASIp2 Windows x86_64 os: windows-2025 - clang_version: 20.1.8 + clang_version: 22.1.8 args: -DTARGET_TRIPLE=wasm32-wasip2 # Other versions of LLVM @@ -74,7 +74,7 @@ jobs: - name: Test wasm32-wasip2 os: ubuntu-24.04 - clang_version: 20 + clang_version: 22 test: true upload: wasm32-wasip2 args: -DTARGET_TRIPLE=wasm32-wasip2 @@ -113,7 +113,7 @@ jobs: - name: Test wasm32-wasip2 (debug) os: ubuntu-24.04 - clang_version: 20 + clang_version: 22 test: true args: -DCMAKE_BUILD_TYPE=Debug -DTARGET_TRIPLE=wasm32-wasip2 @@ -125,14 +125,14 @@ jobs: - name: Test wasm32-wasip3 os: ubuntu-24.04 - clang_version: 20 + clang_version: 22 test: true upload: wasm32-wasip3 args: -DTARGET_TRIPLE=wasm32-wasip3 - name: Test wasm32-wasip3 (debug) os: ubuntu-24.04 - clang_version: 20 + clang_version: 22 test: true args: -DCMAKE_BUILD_TYPE=Debug -DTARGET_TRIPLE=wasm32-wasip3 @@ -156,19 +156,19 @@ jobs: - name: Test LTO os: ubuntu-24.04 - clang_version: 20 + clang_version: 22 test: true args: -DLTO=full -DTARGET_TRIPLE=wasm32-wasip2 -DCHECK_SYMBOLS=OFF - name: Test wasip2 on macOS os: macos-15 - clang_version: wasi-sdk-30 + clang_version: wasi-sdk-33 test: true args: -DTARGET_TRIPLE=wasm32-wasip2 -DCMAKE_C_COMPILER_WORKS=ON - name: Test wasip2 on Windows os: windows-2025 - clang_version: 20.1.8 + clang_version: 22.1.8 test: true args: -DTARGET_TRIPLE=wasm32-wasip2 @@ -248,11 +248,16 @@ jobs: - uses: actions/checkout@v6 with: submodules: true - - run: cmake -S . -B build -DBUILD_TESTS=ON -G Ninja -DCMAKE_C_COMPILER=clang + - uses: ./.github/actions/setup + with: + clang_version: 22 + - run: cmake -S . -B build -DBUILD_TESTS=ON -G Ninja -DCMAKE_C_COMPILER=clang-22 + - run: ninja -C build format-check + - run: cmake -S . -B build -DTARGET_TRIPLE=wasm32-wasip1-threads - run: ninja -C build format-check - run: cmake -S . -B build -DTARGET_TRIPLE=wasm32-wasip2 - run: ninja -C build format-check - - run: cmake -S . -B build -DTARGET_TRIPLE=wasm32-wasip1-threads + - run: cmake -S . -B build -DTARGET_TRIPLE=wasm32-wasip3 - run: ninja -C build format-check rustfmt: diff --git a/CMakeLists.txt b/CMakeLists.txt index bed06c2a6..a37137d7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,6 +240,15 @@ if(ENABLE_COOP_THREADS) else() endif() +# In wasip2/wasip3 code we're using `defer { ... }`, so make sure that exists. +include(CheckCCompilerFlag) +check_c_compiler_flag(-fdefer-ts HAVE_FDEFER_TS) +if(HAVE_FDEFER_TS) + add_compile_options(-fdefer-ts) +elseif(NOT WASI STREQUAL "p1") + message(FATAL_ERROR "wasip2/wasip3 require clang 22 or later for `-fdefer-ts`") +endif() + # ============================================================================= # Helper functions for adding libraries. diff --git a/cmake/scripts/run-check-symbols.cmake b/cmake/scripts/run-check-symbols.cmake index f66782a06..692c6d2ba 100644 --- a/cmake/scripts/run-check-symbols.cmake +++ b/cmake/scripts/run-check-symbols.cmake @@ -45,6 +45,10 @@ endforeach() list(FILTER defined_symbols INCLUDE REGEX " [A-Z] ") foreach(line IN LISTS defined_symbols) string(REGEX REPLACE ".* [A-Z] " "" symbol "${line}") + # Skip internal debug-only asserts + if (symbol MATCHES "__wasilibc_assert_.*") + continue() + endif() list(APPEND final_defined_symbols ${symbol}) endforeach() list(SORT final_defined_symbols) @@ -55,6 +59,10 @@ list(REMOVE_DUPLICATES final_defined_symbols) list(FILTER undefined_symbols INCLUDE REGEX " U ") foreach(line IN LISTS undefined_symbols) string(REGEX REPLACE ".* U " "" symbol "${line}") + # Skip internal debug-only asserts + if (symbol MATCHES "__wasilibc_assert_.*") + continue() + endif() list(APPEND final_undefined_symbols ${symbol}) endforeach() list(SORT final_undefined_symbols) diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt index 4f3d2b2da..f892e6273 100644 --- a/expected/wasm32-wasip2/defined-symbols.txt +++ b/expected/wasm32-wasip2/defined-symbols.txt @@ -295,6 +295,7 @@ __wasilibc_add_tcp_socket __wasilibc_add_udp_socket __wasilibc_cwd __wasilibc_deinitialize_environ +__wasilibc_descriptor_deallocate __wasilibc_dttoif __wasilibc_ensure_environ __wasilibc_environ @@ -522,7 +523,7 @@ ctanl ctime ctime_r descriptor_table_clear -descriptor_table_get_ref +descriptor_table_get descriptor_table_insert descriptor_table_remove descriptor_table_renumber diff --git a/expected/wasm32-wasip2/predefined-macros.txt b/expected/wasm32-wasip2/predefined-macros.txt index f80ecaa38..6f5fffc9f 100644 --- a/expected/wasm32-wasip2/predefined-macros.txt +++ b/expected/wasm32-wasip2/predefined-macros.txt @@ -2978,6 +2978,7 @@ #define __SIZE_TYPE__ long unsigned int #define __SIZE_WIDTH__ 32 #define __STDARG_H +#define __STDC_DEFER_TS25755__ 1 #define __STDC_HOSTED__ 1 #define __STDC_IEC_559__ 1 #define __STDC_ISO_10646__ 201206L diff --git a/expected/wasm32-wasip3-coop/defined-symbols.txt b/expected/wasm32-wasip3-coop/defined-symbols.txt index 7f15bf455..8c629d462 100644 --- a/expected/wasm32-wasip3-coop/defined-symbols.txt +++ b/expected/wasm32-wasip3-coop/defined-symbols.txt @@ -307,6 +307,7 @@ __wasilibc_add_tcp_socket __wasilibc_add_udp_socket __wasilibc_cwd __wasilibc_deinitialize_environ +__wasilibc_descriptor_deallocate __wasilibc_dttoif __wasilibc_ensure_environ __wasilibc_environ @@ -552,7 +553,7 @@ ctanl ctime ctime_r descriptor_table_clear -descriptor_table_get_ref +descriptor_table_get descriptor_table_insert descriptor_table_remove descriptor_table_renumber diff --git a/expected/wasm32-wasip3-coop/predefined-macros.txt b/expected/wasm32-wasip3-coop/predefined-macros.txt index 613e524cd..7c1f93486 100644 --- a/expected/wasm32-wasip3-coop/predefined-macros.txt +++ b/expected/wasm32-wasip3-coop/predefined-macros.txt @@ -2989,6 +2989,7 @@ #define __SIZE_TYPE__ long unsigned int #define __SIZE_WIDTH__ 32 #define __STDARG_H +#define __STDC_DEFER_TS25755__ 1 #define __STDC_HOSTED__ 1 #define __STDC_IEC_559__ 1 #define __STDC_ISO_10646__ 201206L diff --git a/expected/wasm32-wasip3/defined-symbols.txt b/expected/wasm32-wasip3/defined-symbols.txt index e41e805b3..aa6575311 100644 --- a/expected/wasm32-wasip3/defined-symbols.txt +++ b/expected/wasm32-wasip3/defined-symbols.txt @@ -294,6 +294,7 @@ __wasilibc_add_tcp_socket __wasilibc_add_udp_socket __wasilibc_cwd __wasilibc_deinitialize_environ +__wasilibc_descriptor_deallocate __wasilibc_dttoif __wasilibc_ensure_environ __wasilibc_environ @@ -529,7 +530,7 @@ ctanl ctime ctime_r descriptor_table_clear -descriptor_table_get_ref +descriptor_table_get descriptor_table_insert descriptor_table_remove descriptor_table_renumber diff --git a/expected/wasm32-wasip3/predefined-macros.txt b/expected/wasm32-wasip3/predefined-macros.txt index f5ec3bd20..950d5dfaf 100644 --- a/expected/wasm32-wasip3/predefined-macros.txt +++ b/expected/wasm32-wasip3/predefined-macros.txt @@ -2984,6 +2984,7 @@ #define __SIZE_TYPE__ long unsigned int #define __SIZE_WIDTH__ 32 #define __STDARG_H +#define __STDC_DEFER_TS25755__ 1 #define __STDC_HOSTED__ 1 #define __STDC_IEC_559__ 1 #define __STDC_ISO_10646__ 201206L diff --git a/libc-bottom-half/CMakeLists.txt b/libc-bottom-half/CMakeLists.txt index fe7cca273..0be979006 100644 --- a/libc-bottom-half/CMakeLists.txt +++ b/libc-bottom-half/CMakeLists.txt @@ -219,6 +219,7 @@ foreach(file crt/crt1-command.c target_link_libraries(${stem} PRIVATE musl-top-half-interface) set_pic(${stem}) target_compile_options(${stem} PRIVATE -fvisibility=default -fno-lto) + target_include_directories(${stem} PRIVATE headers/private) endforeach() set(crt_sysroot ${SYSROOT}/lib/${TARGET_TRIPLE}) diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c index d24b8b60f..89f61ba65 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #else @@ -50,12 +51,14 @@ DIR *fdopendir(int fd) { dirp->dirent_size = 1; return dirp; #elif defined(__wasip2__) || defined(__wasip3__) + defer free(dirp); + // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) { - free(dirp); + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return NULL; - } + defer descriptor_table_entry_dec(entry); // Read the directory #if defined(__wasip2__) @@ -65,7 +68,6 @@ DIR *fdopendir(int fd) { &result, &error_code); if (!ok) { - free(dirp); translate_error(&error_code); return NULL; } @@ -80,7 +82,9 @@ DIR *fdopendir(int fd) { dirp->offset = 0; dirp->dirent = NULL; dirp->dirent_size = 1; - return dirp; + DIR *ret = dirp; + dirp = NULL; + return ret; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c index 3d296eae5..277e9e313 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c @@ -15,6 +15,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -147,9 +148,19 @@ struct dirent *readdir(DIR *dirp) { } #elif defined(__wasip2__) || defined(__wasip3__) - -static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t *handle) { - if (fd_to_file_handle(dirp->fd, handle) < 0) +// Ensures that `dirp` has the necessary streams prepared to start reading +// directory entries. +// +// On success returns `entry`, the owned entry for this operation that must be +// deallocated with `descriptor_table_entry_dec` when done, and `handle`, the +// WASI file that's being used. Additionally `dirp`'s streams are filled and +// ready for use. +// +// On failure returns -1 and `entry` need not be deallocated. +static int ensure_has_directory_stream(DIR *dirp, + descriptor_table_entry_t *entry, + filesystem_borrow_descriptor_t *handle) { + if (fd_to_file_handle(dirp->fd, entry, handle) < 0) return -1; #ifdef __wasip2__ @@ -162,6 +173,7 @@ static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t &error_code); if (!ok) { translate_error(&error_code); + descriptor_table_entry_dec(*entry); return -1; } #elif defined(__wasip3__) @@ -176,9 +188,11 @@ static struct dirent *readdir_next(DIR *dirp) { filesystem_metadata_hash_value_t metadata; filesystem_error_code_t error_code; filesystem_borrow_descriptor_t dir_handle; + descriptor_table_entry_t entry; - if (ensure_has_directory_stream(dirp, &dir_handle) < 0) + if (ensure_has_directory_stream(dirp, &entry, &dir_handle) < 0) return NULL; + defer descriptor_table_entry_dec(entry); // Yield '.' first if the offset is 0. Note that `d_ino` is from the metadata // hash of the directory itself. diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c index da9051a88..52012c5e7 100644 --- a/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c +++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c @@ -8,15 +8,17 @@ #include #ifndef __wasip1__ +#include #include #include #endif int fcntl(int fildes, int cmd, ...) { #if defined(__wasip2__) || defined(__wasip3__) - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (entry == NULL) + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) return -1; + defer descriptor_table_entry_dec(entry); #endif switch (cmd) { @@ -52,11 +54,11 @@ int fcntl(int fildes, int cmd, ...) { } return oflags; #elif defined(__wasip2__) || defined(__wasip3__) - if (!entry->vtable->fcntl_getfl) { + if (!entry.vtable->fcntl_getfl) { errno = EINVAL; return -1; } - return entry->vtable->fcntl_getfl(entry->data); + return entry.vtable->fcntl_getfl(entry.data); #else # error "Unknown WASI version" #endif @@ -77,11 +79,11 @@ int fcntl(int fildes, int cmd, ...) { return -1; } #elif defined(__wasip2__) || defined(__wasip3__) - if (!entry->vtable->fcntl_setfl) { + if (!entry.vtable->fcntl_setfl) { errno = EINVAL; return -1; } - return entry->vtable->fcntl_setfl(entry->data, flags); + return entry.vtable->fcntl_setfl(entry.data, flags); #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c index 7fd5e6073..e0627eb01 100644 --- a/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c +++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c @@ -10,6 +10,7 @@ #include #ifndef __wasip1__ +#include #include #include #include @@ -136,8 +137,10 @@ int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) { // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Construct a WASI string for the path wasi_string_t wasi_path; diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c index 53141d8a1..0e9ae05e8 100644 --- a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c +++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -32,8 +33,10 @@ int posix_fadvise(int fd, off_t offset, off_t len, int advice) { return __wasi_fd_advise(fd, offset, len, advice); #elif defined(__wasip2__) || defined(__wasip3__) filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return EBADF; + defer descriptor_table_entry_dec(entry); if (file_handle.__handle == 0) { errno = EBADF; return EBADF; diff --git a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c index 791d881cb..9c792c7d3 100644 --- a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c +++ b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c @@ -9,6 +9,10 @@ #include #include +#ifndef __wasip1__ +#include +#endif + #if defined(__wasip1__) static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { // Construct events for poll(). @@ -134,6 +138,7 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { typedef struct { struct pollfd *pollfd; + descriptor_table_entry_t entry; short events; } state_t; @@ -144,7 +149,8 @@ struct poll_state_t { size_t cap; int event_count; - struct pollfd *pollfd; + struct pollfd *cur_pollfd; + descriptor_table_entry_t *cur_entry; }; int __wasilibc_poll_add(poll_state_t *state, short events, @@ -153,19 +159,24 @@ int __wasilibc_poll_add(poll_state_t *state, short events, errno = ENOMEM; return -1; } - state->states[state->len].pollfd = state->pollfd; + assert(state->cur_pollfd); + assert(state->cur_entry); + state->states[state->len].pollfd = state->cur_pollfd; state->states[state->len].events = events; + descriptor_table_entry_inc(*state->cur_entry); + state->states[state->len].entry = *state->cur_entry; state->pollables[state->len] = pollable; state->len += 1; return 0; } void __wasilibc_poll_ready(poll_state_t *state, short events) { + assert(state->cur_pollfd); if (events != 0) { - if (state->pollfd->revents == 0) { + if (state->cur_pollfd->revents == 0) { ++state->event_count; } - state->pollfd->revents |= events; + state->cur_pollfd->revents |= events; } } @@ -181,23 +192,33 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { state.len = 0; state.cap = max_pollables; state.event_count = 0; - state.pollfd = fds; + state.cur_pollfd = NULL; + state.cur_entry = NULL; + + defer { + for (size_t i = 0; i < state.len; i++) + descriptor_table_entry_dec(state.states[i].entry); + } for (size_t i = 0; i < nfds; ++i) { struct pollfd *pollfd = fds + i; if (pollfd->fd < 0) continue; - state.pollfd = pollfd; - descriptor_table_entry_t *entry = descriptor_table_get_ref(pollfd->fd); - if (!entry) { - errno = EBADF; + descriptor_table_entry_t entry; + if (descriptor_table_get(pollfd->fd, &entry) < 0) return -1; + state.cur_pollfd = pollfd; + state.cur_entry = &entry; + defer { + state.cur_pollfd = NULL; + state.cur_entry = NULL; + descriptor_table_entry_dec(entry); } // If this descriptor has a custom registration function then // use that exclusively. - if (entry->vtable->poll_register) { - if (entry->vtable->poll_register(entry->data, &state, pollfd->events) < 0) + if (entry.vtable->poll_register) { + if (entry.vtable->poll_register(entry.data, &state, pollfd->events) < 0) return -1; continue; } @@ -213,9 +234,9 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { } if (events & POLLRDNORM) { - if (entry->vtable->get_read_stream) { + if (entry.vtable->get_read_stream) { wasi_read_t read; - if (entry->vtable->get_read_stream(entry->data, &read) < 0) + if (entry.vtable->get_read_stream(entry.data, &read) < 0) return -1; if (__wasilibc_poll_add_input_stream(&state, read.input, read.pollable) < 0) return -1; @@ -226,9 +247,9 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { } if (events & POLLWRNORM) { - if (entry->vtable->get_write_stream) { + if (entry.vtable->get_write_stream) { wasi_write_t write; - if (entry->vtable->get_write_stream(entry->data, &write) < 0) + if (entry.vtable->get_write_stream(entry.data, &write) < 0) return -1; if (__wasilibc_poll_add_output_stream(&state, write.output, write.pollable) < 0) return -1; @@ -243,26 +264,31 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { return state.event_count; } - poll_own_pollable_t timeout_pollable; + poll_own_pollable_t timeout_pollable = {0}; size_t pollable_count = state.len; if (timeout >= 0) { timeout_pollable = monotonic_clock_subscribe_duration( ((monotonic_clock_duration_t)timeout) * 1000000); pollables[pollable_count++] = poll_borrow_pollable(timeout_pollable); } + defer { + if (timeout_pollable.__handle != 0) + poll_pollable_drop_own(timeout_pollable); + } wasip2_list_u32_t ready; poll_list_borrow_pollable_t list = {.ptr = pollables, .len = pollable_count}; poll_poll(&list, &ready); + defer wasip2_list_u32_free(&ready); for (size_t i = 0; i < ready.len; ++i) { size_t index = ready.ptr[i]; if (index >= state.len) continue; state_t *ready_state = &states[index]; - state.pollfd = ready_state->pollfd; - descriptor_table_entry_t *entry = - descriptor_table_get_ref(ready_state->pollfd->fd); + descriptor_table_entry_t *entry = &ready_state->entry; + state.cur_pollfd = ready_state->pollfd; + state.cur_entry = entry; if (entry->vtable->poll_finish) { entry->vtable->poll_finish(entry->data, &state, ready_state->events); } else { @@ -270,18 +296,13 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { } } - wasip2_list_u32_free(&ready); - - if (timeout >= 0) { - poll_pollable_drop_own(timeout_pollable); - } - return state.event_count; } #elif defined(__wasip3__) typedef struct { + descriptor_table_entry_t entry; struct pollfd *pollfd; uint32_t waitable; // Callback/data pointer to invoke when `waitable` receives an event, and that @@ -298,7 +319,8 @@ struct poll_state_t { wasip3_waitable_set_t set; int event_count; - struct pollfd *pollfd; + struct pollfd *cur_pollfd; + descriptor_table_entry_t *cur_entry; }; int __wasilibc_poll_add(poll_state_t *state, uint32_t waitable, @@ -307,22 +329,27 @@ int __wasilibc_poll_add(poll_state_t *state, uint32_t waitable, errno = ENOMEM; return -1; } - state->states[state->len].pollfd = state->pollfd; + assert(state->cur_pollfd); + assert(state->cur_entry); + state->states[state->len].pollfd = state->cur_pollfd; state->states[state->len].waitable = waitable; state->states[state->len].ready = ready; state->states[state->len].ready_data = ready_data; + descriptor_table_entry_inc(*state->cur_entry); + state->states[state->len].entry = *state->cur_entry; wasip3_waitable_join(waitable, state->set); state->len += 1; return 0; } void __wasilibc_poll_ready(poll_state_t *state, short events) { - events = events & state->pollfd->events; + assert(state->cur_pollfd); + events = events & state->cur_pollfd->events; if (events != 0) { - if (state->pollfd->revents == 0) { + if (state->cur_pollfd->revents == 0) { ++state->event_count; } - state->pollfd->revents |= events; + state->cur_pollfd->revents |= events; } } @@ -339,27 +366,53 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { state.len = 0; state.cap = max_pollables; state.event_count = 0; - state.pollfd = fds; + state.cur_pollfd = NULL; + state.cur_entry = NULL; + + // Schedule cleanup of `state`, notably every waitable that's still in it, all + // the entries, and then the waitable-set itself. + defer { + for (size_t i = 0; i < state.len; i++) { + uint32_t waitable = state.states[i].waitable; + if (waitable) + wasip3_waitable_join(waitable, 0); + descriptor_table_entry_dec(state.states[i].entry); + } + wasip3_waitable_set_drop(state.set); + } - int ret = -1; + // Tracks the timeout, if any, and the subtask being used to wait on that. + // Additioanlly schedule cleanup of this subtask. wasip3_subtask_t timeout_subtask = 0; + defer { + if (timeout_subtask != 0) { + wasip3_waitable_join(timeout_subtask, 0); + wasip3_subtask_cancel(timeout_subtask); + wasip3_subtask_drop(timeout_subtask); + } + } for (size_t i = 0; i < nfds; ++i) { struct pollfd *pollfd = fds + i; if (pollfd->fd < 0) continue; - state.pollfd = pollfd; - descriptor_table_entry_t *entry = descriptor_table_get_ref(pollfd->fd); - if (!entry) { - errno = EBADF; - goto out; + descriptor_table_entry_t entry; + if (descriptor_table_get(pollfd->fd, &entry) < 0) + return -1; + defer descriptor_table_entry_dec(entry); + + state.cur_pollfd = pollfd; + state.cur_entry = &entry; + defer { + state.cur_pollfd = NULL; + state.cur_entry = NULL; } // If this descriptor has a custom registration function then // use that exclusively. - if (entry->vtable->poll_register) { - if (entry->vtable->poll_register(entry->data, &state, pollfd->events) < 0) - goto out; + if (entry.vtable->poll_register) { + if (entry.vtable->poll_register(entry.data, &state, pollfd->events) < 0) + return -1; continue; } @@ -370,32 +423,32 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { // below, but everything else is unsupported. if (events & ~(POLLRDNORM | POLLWRNORM)) { errno = EOPNOTSUPP; - goto out; + return -1; } if (events & POLLRDNORM) { - if (entry->vtable->get_read_stream) { + if (entry.vtable->get_read_stream) { wasi_read_t read; - if (entry->vtable->get_read_stream(entry->data, &read) < 0) - goto out; + if (entry.vtable->get_read_stream(entry.data, &read) < 0) + return -1; if (__wasilibc_read_poll(read.state, &state) < 0) - goto out; + return -1; } else { errno = EOPNOTSUPP; - goto out; + return -1; } } if (events & POLLWRNORM) { - if (entry->vtable->get_write_stream) { + if (entry.vtable->get_write_stream) { wasi_write_t write; - if (entry->vtable->get_write_stream(entry->data, &write) < 0) - goto out; + if (entry.vtable->get_write_stream(entry.data, &write) < 0) + return -1; if (__wasilibc_write_poll(write.state, &state) < 0) - goto out; + return -1; } else { errno = EOPNOTSUPP; - goto out; + return -1; } } } @@ -413,7 +466,8 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { // Note that in `out`, the exit of this function, the subtask is cleaned up // if it's still in-progress. if (timeout > 0) { - wasip3_subtask_status_t status = monotonic_clock_wait_for(timeout * 1000000); + uint64_t timeout_ns = (uint64_t)timeout * 1000000; + wasip3_subtask_status_t status = monotonic_clock_wait_for(timeout_ns); if (WASIP3_SUBTASK_STATE(status) == WASIP3_SUBTASK_RETURNED) { timeout = 0; } else { @@ -469,7 +523,7 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { state_t *p = &state.states[i]; if (p->waitable != event.waitable) continue; - state.pollfd = p->pollfd; + state.cur_pollfd = p->pollfd; // Remove this waitable from the `waitable-set` as the `ready` // operation might end up deleting the handle. Set the list here to 0 // so it's not removed down below. @@ -480,6 +534,7 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { wasip3_waitable_join(p->waitable, 0); p->waitable = 0; p->ready(p->ready_data, &state, &event); + state.cur_pollfd = NULL; } } @@ -489,22 +544,7 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { wasip3_waitable_set_poll(state.set, &event); } - ret = state.event_count; - -out: - if (timeout_subtask != 0) { - wasip3_waitable_join(timeout_subtask, 0); - wasip3_subtask_cancel(timeout_subtask); - wasip3_subtask_drop(timeout_subtask); - } - for (size_t i = 0; i < state.len; i++) { - uint32_t waitable = state.states[i].waitable; - if (waitable) - wasip3_waitable_join(waitable, 0); - } - wasip3_waitable_set_drop(state.set); - - return ret; + return state.event_count; } #else diff --git a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c index d487375ae..2d39157ed 100644 --- a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c +++ b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -22,12 +23,16 @@ int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptors to internal handles filesystem_borrow_descriptor_t old_file_handle; - if (fd_to_file_handle(oldfd, &old_file_handle) < 0) + descriptor_table_entry_t old_entry; + if (fd_to_file_handle(oldfd, &old_entry, &old_file_handle) < 0) return -1; + defer descriptor_table_entry_dec(old_entry); filesystem_borrow_descriptor_t new_file_handle; - if (fd_to_file_handle(newfd, &new_file_handle) < 0) + descriptor_table_entry_t new_entry; + if (fd_to_file_handle(newfd, &new_entry, &new_file_handle) < 0) return -1; + defer descriptor_table_entry_dec(new_entry); // Convert the strings into WASI strings wasi_string_t old_path, new_path; diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c b/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c index bc360249e..8da1a6b42 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c @@ -9,6 +9,10 @@ #include #include +#ifndef __wasip1__ +#include +#endif + int ioctl(int fildes, int request, ...) { switch (request) { case FIONREAD: { @@ -90,17 +94,20 @@ int ioctl(int fildes, int request, ...) { } return 0; #elif defined(__wasip2__) || defined(__wasip3__) - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) + return -1; + defer descriptor_table_entry_dec(entry); va_list ap; va_start(ap, request); bool blocking = *va_arg(ap, const int *) == 0; va_end(ap); - if (!entry->vtable->set_blocking) { + if (!entry.vtable->set_blocking) { errno = EINVAL; return -1; } - return entry->vtable->set_blocking(entry->data, blocking); + return entry.vtable->set_blocking(entry.data, blocking); #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c index 3a672067e..393652a90 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c @@ -8,6 +8,10 @@ #include #include +#ifndef __wasip1__ +#include +#endif + int fstat(int fildes, struct stat *buf) { #if defined(__wasip1__) __wasi_filestat_t internal_stat; @@ -20,10 +24,11 @@ int fstat(int fildes, struct stat *buf) { return 0; #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) return -1; - return entry->vtable->fstat(entry->data, buf); + defer descriptor_table_entry_dec(entry); + return entry.vtable->fstat(entry.data, buf); #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c index 8ceae02eb..da52634a8 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c @@ -11,6 +11,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -38,8 +39,10 @@ int __wasilibc_nocwd_fstatat(int fd, const char *restrict path, struct stat *res #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert the string into a Wasm string wasi_string_t path_wasm_string; diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c index d77d36c3e..07249bc4b 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c @@ -8,6 +8,7 @@ #include "stat_impl.h" #ifndef __wasip1__ +#include #include #include #endif @@ -32,8 +33,10 @@ int futimens(int fd, const struct timespec *times) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert timestamps and extract NOW/OMIT flags. filesystem_new_timestamp_t new_timestamp_atim; diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c index 63975171c..3c35470b4 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -24,8 +25,10 @@ int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Create the directory filesystem_error_code_t error; diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c index 619aa701a..b113acfba 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c @@ -10,6 +10,7 @@ #include "stat_impl.h" #ifndef __wasip1__ +#include #include #include #endif @@ -41,8 +42,10 @@ int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec t #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert timestamps and extract NOW/OMIT flags. filesystem_new_timestamp_t new_timestamp_atim; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c index 28ee15816..fe8cfbb2c 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c @@ -9,6 +9,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -59,8 +60,10 @@ int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) { // Translate the file descriptor to an internal handle // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert the string into a WASI string wasi_string_t wasi_path; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c index 7462d0628..7a2a7dfff 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -22,8 +23,10 @@ int fdatasync(int fildes) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Sync the data filesystem_error_code_t error_code; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c index 812bb06ca..d22dc744d 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c @@ -7,6 +7,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -21,8 +22,10 @@ int fsync(int fildes) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Sync the file filesystem_error_code_t error_code; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c index c7b0eeb76..963c70415 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c @@ -7,6 +7,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -27,8 +28,10 @@ int ftruncate(int fildes, off_t length) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal file handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); filesystem_error_code_t error_code; if (!filesystem_method_descriptor_set_size(file_handle, length, &error_code)) { diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c index f1c3a9239..9c8d5bc48 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c @@ -9,6 +9,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -29,10 +30,14 @@ int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *pat #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptors to internal handles filesystem_borrow_descriptor_t file_handle1, file_handle2; - if (fd_to_file_handle(fd1, &file_handle1) < 0) + descriptor_table_entry_t entry1; + if (fd_to_file_handle(fd1, &entry1, &file_handle1) < 0) return -1; - if (fd_to_file_handle(fd2, &file_handle2) < 0) + defer descriptor_table_entry_dec(entry1); + descriptor_table_entry_t entry2; + if (fd_to_file_handle(fd2, &entry2, &file_handle2) < 0) return -1; + defer descriptor_table_entry_dec(entry2); // Convert the strings into WASI strings wasi_string_t path1_wasi, path2_wasi; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c index 1305686a5..3c1d0f17e 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #include @@ -31,14 +32,15 @@ off_t __lseek(int fildes, off_t offset, int whence) { return new_offset; #elif defined(__wasip2__) || defined(__wasip3__) // Look up a stream for fildes - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) return -1; - if (!entry->vtable->seek) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->seek) { errno = EINVAL; return -1; } - return entry->vtable->seek(entry->data, offset, whence); + return entry.vtable->seek(entry.data, offset, whence); #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c index 630048f04..466c2a76c 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c @@ -7,6 +7,7 @@ #include #ifndef __wasip1__ +#include #include #include #include @@ -43,8 +44,10 @@ ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset) { #elif defined(__wasip2__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Set up a WASI tuple to receive the results wasip2_tuple2_list_u8_bool_t contents; @@ -71,8 +74,10 @@ ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset) { return bytes_read; #elif defined(__wasip3__) filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Use `read-via-stream` to acquire a stream of data at this specified file // offset, then issue a read and wait for it to finish. diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c index 060f1fb98..a20b6e963 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c @@ -7,6 +7,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -42,8 +43,10 @@ ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) { #elif defined(__wasip2__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert `buf` to a WASI byte list wasip2_list_u8_t contents; @@ -67,8 +70,10 @@ ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) { return bytes_written; #elif defined(__wasip3__) filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fildes, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fildes, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Create a read/write stream, use `write-via-stream` to start writing, // then perform the write to see how much was accepted. diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c index d2f43e14f..93861bd66 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c @@ -7,6 +7,7 @@ #include #ifndef __wasip1__ +#include #include #include #include @@ -24,17 +25,18 @@ ssize_t read(int fildes, void *buf, size_t nbyte) { } return bytes_read; #else - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) return -1; - if (entry->vtable->get_read_stream) { + defer descriptor_table_entry_dec(entry); + if (entry.vtable->get_read_stream) { wasi_read_t read; - if (entry->vtable->get_read_stream(entry->data, &read) < 0) + if (entry.vtable->get_read_stream(entry.data, &read) < 0) return -1; return __wasilibc_read(&read, buf, nbyte); } - if (entry->vtable->recvfrom) - return entry->vtable->recvfrom(entry->data, buf, nbyte, 0, NULL, NULL); + if (entry.vtable->recvfrom) + return entry.vtable->recvfrom(entry.data, buf, nbyte, 0, NULL, NULL); errno = EOPNOTSUPP; return -1; #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c index b23e7ccb7..ef0f021ef 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -26,8 +27,10 @@ ssize_t __wasilibc_nocwd_readlinkat(int fd, const char *restrict path, char *res #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert the path into a WASI path wasi_string_t wasi_path, link_source; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c index b1a2fbf75..0dd45201e 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c @@ -8,6 +8,7 @@ #include #ifndef __wasip1__ +#include #include #include #endif @@ -22,8 +23,10 @@ int __wasilibc_nocwd_symlinkat(const char *path1, int fd, const char *path2) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Convert the paths into WASI paths wasi_string_t path1_wasi, path2_wasi; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c index c7e010e37..d4f07a682 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c @@ -7,6 +7,7 @@ #include #ifndef __wasip1__ +#include #include #include #include @@ -25,17 +26,18 @@ ssize_t write(int fildes, const void *buf, size_t nbyte) { } return bytes_written; #else - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) return -1; - if (entry->vtable->get_write_stream) { + defer descriptor_table_entry_dec(entry); + if (entry.vtable->get_write_stream) { wasi_write_t write; - if (entry->vtable->get_write_stream(entry->data, &write) < 0) + if (entry.vtable->get_write_stream(entry.data, &write) < 0) return -1; return __wasilibc_write(&write, buf, nbyte); } - if (entry->vtable->sendto) - return entry->vtable->sendto(entry->data, buf, nbyte, 0, NULL, 0); + if (entry.vtable->sendto) + return entry.vtable->sendto(entry.data, buf, nbyte, 0, NULL, 0); errno = EOPNOTSUPP; return -1; #endif diff --git a/libc-bottom-half/crt/crt1-command.c b/libc-bottom-half/crt/crt1-command.c index e79acf696..b25cfc93a 100644 --- a/libc-bottom-half/crt/crt1-command.c +++ b/libc-bottom-half/crt/crt1-command.c @@ -3,6 +3,7 @@ #endif #include +#include extern void __wasm_call_ctors(void); extern int __main_void(void); @@ -69,6 +70,7 @@ __attribute__((export_name("wasi:cli/run@0.3.0#run"))) int _start(void) __wasi_proc_exit(r); } #elif defined(__wasip2__) || defined(__wasip3__) + __wasilibc_assert_no_descriptor_leaks(); return r != 0; #else #error "Unsupported WASI version" diff --git a/libc-bottom-half/headers/private/wasi/descriptor_table.h b/libc-bottom-half/headers/private/wasi/descriptor_table.h index 35a460683..d540b6cf3 100644 --- a/libc-bottom-half/headers/private/wasi/descriptor_table.h +++ b/libc-bottom-half/headers/private/wasi/descriptor_table.h @@ -231,10 +231,16 @@ typedef struct descriptor_vtable_t { #endif // __wasip2__ } descriptor_vtable_t; +/// Reference count structure that must be at the beginning of all data passed +/// to `descriptor_table_insert`. +typedef struct { + unsigned cnt; +} descriptor_refcnt_t; + /// A "fat pointer" which is placed inside of the descriptor table. typedef struct { /// Arbitrary descriptor-specific data passed to `vtable` function pointer. - void *data; + descriptor_refcnt_t *data; /// Definition of various operations for this descriptor. descriptor_vtable_t *vtable; } descriptor_table_entry_t; @@ -242,14 +248,18 @@ typedef struct { /// Inserts the `entry` provided into the descriptor table, returning the /// integer file descriptor used to refer to it. /// +/// Note that `entry` is expected to have a reference count of 0 (freshly +/// initialized), and it'll get initialized within this function. +/// /// On failure returns -1, sets `errno`, and runs `entry`'s destructor. int descriptor_table_insert(descriptor_table_entry_t entry); /// Looks up a descriptor by its file descriptor. /// -/// On success returns a non-null value of the entry in the table. On failure -/// returns `NULL` and sets errno. -descriptor_table_entry_t *descriptor_table_get_ref(int fd); +/// On success returns 0 and `entry` is filled in with a strong reference to +/// the new entry. Callers must call `descriptor_table_entry_dec` when they're +/// done with the entry. On failure -1 is returned an `errno` is set. +int descriptor_table_get(int fd, descriptor_table_entry_t *entry); /// Removes the specified file descriptor from the table. /// @@ -267,6 +277,35 @@ int descriptor_table_renumber(int fd, int newfd); /// Removes all file descriptors from the table, running their destructors. void descriptor_table_clear(); +/// Increment the reference count of the `entry` provided. +static inline void descriptor_table_entry_inc(descriptor_table_entry_t entry) { + assert(entry.data->cnt > 0); + entry.data->cnt++; +} + +/// Slow-path deallocation routine of `descriptor_table_entry_dec`. +/// +/// Assumes refcount is already 0. +void __wasilibc_descriptor_deallocate(descriptor_table_entry_t entry); + +/// Decrement the reference count of the `entry` provided, running deallocation +/// if the cnt reaches 0. +static inline void descriptor_table_entry_dec(descriptor_table_entry_t entry) { + assert(entry.data->cnt > 0); + entry.data->cnt--; + if (entry.data->cnt == 0) + __wasilibc_descriptor_deallocate(entry); +} + +// Helper function used at program-exit time to assert that there are no +// descriptor leaks when libc is itself built in debug mode. Note that this is +// a noop when compiled with `-DNDEBUG`. +#ifdef NDEBUG +static inline void __wasilibc_assert_no_descriptor_leaks(void) {} +#else +void __wasilibc_assert_no_descriptor_leaks(void); +#endif + #endif // __wasip1__ #endif // DESCRIPTOR_TABLE_H diff --git a/libc-bottom-half/headers/private/wasi/file_utils.h b/libc-bottom-half/headers/private/wasi/file_utils.h index 5b12baf85..23f8aa6f6 100644 --- a/libc-bottom-half/headers/private/wasi/file_utils.h +++ b/libc-bottom-half/headers/private/wasi/file_utils.h @@ -56,17 +56,27 @@ typedef wasip3_string_t wasi_string_t; // Returns -1 and sets errno to `ENOENT` if `s` is not valid utf-8. int wasi_string_from_c(const char *s, wasi_string_t *out); -// Succeed only if fd is bound to a file handle in the descriptor table -static inline int fd_to_file_handle(int fd, +// Looks up the `fd` specified and acquires the WASI file handle within it, +// storing it into `result`. +// +// On success 0 is returned and `entry` and `result` are filled in. Callers must +// use `descriptor_table_entry_dec` on `entry`. +// +// On failure -1, errno is set, and nothing else need be done. +static inline int fd_to_file_handle(int fd, descriptor_table_entry_t *entry, filesystem_borrow_descriptor_t *result) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(fd); - if (entry == NULL) + if (descriptor_table_get(fd, entry) < 0) return -1; if (!entry->vtable->get_file) { + descriptor_table_entry_dec(*entry); errno = EINVAL; return -1; } - return entry->vtable->get_file(entry->data, result); + if (entry->vtable->get_file(entry->data, result) < 0) { + descriptor_table_entry_dec(*entry); + return -1; + } + return 0; } // Reads from `read` into `buf`/`len` diff --git a/libc-bottom-half/headers/private/wasi/tcp.h b/libc-bottom-half/headers/private/wasi/tcp.h index 6460b29a6..8a13a26cf 100644 --- a/libc-bottom-half/headers/private/wasi/tcp.h +++ b/libc-bottom-half/headers/private/wasi/tcp.h @@ -103,6 +103,7 @@ typedef struct { } tcp_socket_state_t; typedef struct { + descriptor_refcnt_t refcnt; sockets_own_tcp_socket_t socket; tcp_socket_state_t state; #ifdef __wasip2__ diff --git a/libc-bottom-half/sources/__wasilibc_rmdirat.c b/libc-bottom-half/sources/__wasilibc_rmdirat.c index a0347a194..98dd05d44 100644 --- a/libc-bottom-half/sources/__wasilibc_rmdirat.c +++ b/libc-bottom-half/sources/__wasilibc_rmdirat.c @@ -4,6 +4,7 @@ #ifndef __wasip1__ #include +#include #include #include #endif @@ -18,8 +19,10 @@ int __wasilibc_nocwd___wasilibc_rmdirat(int fd, const char *path) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; - if (fd_to_file_handle(fd, &file_handle) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file_handle) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Create a WASI string for the path wasi_string_t wasi_path; diff --git a/libc-bottom-half/sources/__wasilibc_tell.c b/libc-bottom-half/sources/__wasilibc_tell.c index 658dfedb7..3efc1b2e8 100644 --- a/libc-bottom-half/sources/__wasilibc_tell.c +++ b/libc-bottom-half/sources/__wasilibc_tell.c @@ -4,6 +4,7 @@ #ifndef __wasip1__ #include +#include #include #include #endif @@ -21,14 +22,15 @@ off_t __wasilibc_tell(int fildes) { return offset; #elif defined(__wasip2__) || defined(__wasip3__) // Look up a stream for fildes - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fildes, &entry) < 0) return -1; - if (!entry->vtable->seek) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->seek) { errno = EINVAL; return -1; } - return entry->vtable->seek(entry->data, 0, SEEK_CUR); + return entry.vtable->seek(entry.data, 0, SEEK_CUR); #else #error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/sources/__wasilibc_unlinkat.c b/libc-bottom-half/sources/__wasilibc_unlinkat.c index 5000fbe13..36b1859ae 100644 --- a/libc-bottom-half/sources/__wasilibc_unlinkat.c +++ b/libc-bottom-half/sources/__wasilibc_unlinkat.c @@ -4,6 +4,7 @@ #ifndef __wasip1__ #include +#include #include #include #endif @@ -19,8 +20,10 @@ int __wasilibc_nocwd___wasilibc_unlinkat(int fd, const char *path) { #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file; - if (fd_to_file_handle(fd, &file) < 0) + descriptor_table_entry_t entry; + if (fd_to_file_handle(fd, &entry, &file) < 0) return -1; + defer descriptor_table_entry_dec(entry); // Create a Wasm string from the path wasi_string_t wasi_path; diff --git a/libc-bottom-half/sources/accept.c b/libc-bottom-half/sources/accept.c index 77d718574..ce70b4176 100644 --- a/libc-bottom-half/sources/accept.c +++ b/libc-bottom-half/sources/accept.c @@ -1,5 +1,6 @@ #include #include +#include #include int accept(int socket, struct sockaddr *restrict addr, @@ -9,12 +10,13 @@ int accept(int socket, struct sockaddr *restrict addr, int accept4(int socket, struct sockaddr *restrict addr, socklen_t *restrict addrlen, int flags) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; - if (!entry->vtable->accept4) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->accept4) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->accept4(entry->data, addr, addrlen, flags); + return entry.vtable->accept4(entry.data, addr, addrlen, flags); } diff --git a/libc-bottom-half/sources/bind.c b/libc-bottom-half/sources/bind.c index ce1b696bd..66f68389d 100644 --- a/libc-bottom-half/sources/bind.c +++ b/libc-bottom-half/sources/bind.c @@ -1,14 +1,16 @@ #include #include +#include #include int bind(int socket, const struct sockaddr *addr, socklen_t addrlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; - if (!entry->vtable->bind) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->bind) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->bind(entry->data, addr, addrlen); + return entry.vtable->bind(entry.data, addr, addrlen); } diff --git a/libc-bottom-half/sources/connect.c b/libc-bottom-half/sources/connect.c index 4dcad58c8..f4e1dacc5 100644 --- a/libc-bottom-half/sources/connect.c +++ b/libc-bottom-half/sources/connect.c @@ -1,14 +1,16 @@ #include #include +#include #include int connect(int fd, const struct sockaddr *addr, socklen_t addrlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(fd); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fd, &entry) < 0) return -1; - if (!entry->vtable->connect) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->connect) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->connect(entry->data, addr, addrlen); + return entry.vtable->connect(entry.data, addr, addrlen); } diff --git a/libc-bottom-half/sources/descriptor_table.c b/libc-bottom-half/sources/descriptor_table.c index 61df01610..a0e08fd53 100644 --- a/libc-bottom-half/sources/descriptor_table.c +++ b/libc-bottom-half/sources/descriptor_table.c @@ -42,7 +42,8 @@ static descriptor_table_t global_table = { * * Returns -1 on failure and sets `errno`. */ -static int allocate(descriptor_table_t *table, descriptor_table_entry_t entry) { +static int table_allocate(descriptor_table_t *table, + descriptor_table_entry_t entry) { // If the table is at its limit, then a new entry needs to be allocated. If // the table's entire allocation capacity has been reached then that must also // be resized. @@ -81,7 +82,8 @@ static int allocate(descriptor_table_t *table, descriptor_table_entry_t entry) { * * Returns -1 on failure and sets `errno`. */ -static descriptor_table_entry_t *lookup(descriptor_table_t *table, int fd) { +static descriptor_table_entry_t *table_lookup(descriptor_table_t *table, + int fd) { if (fd < 0 || (size_t)fd >= table->len) { errno = EBADF; return NULL; @@ -104,8 +106,8 @@ static descriptor_table_entry_t *lookup(descriptor_table_t *table, int fd) { * * Returns -1 on failure and sets `errno`. */ -static int remove(descriptor_table_t *table, int fd, - descriptor_table_entry_t *ret) { +static int table_remove(descriptor_table_t *table, int fd, + descriptor_table_entry_t *ret) { if (fd < 0 || (size_t)fd >= table->len) { errno = EBADF; return -1; @@ -129,8 +131,7 @@ static void clear(descriptor_table_t *table) { for (size_t i = 0; i < table->len; ++i) { descriptor_table_item_t *table_entry = &table->entries[i]; if (table_entry->occupied) { - descriptor_table_entry_t entry = table_entry->entry; - entry.vtable->free(entry.data); + descriptor_table_entry_dec(table_entry->entry); } } if (table->entries) @@ -148,38 +149,95 @@ static int init_stdio() { return __wasilibc_init_stdio(); } +#ifdef NDEBUG +static void live_descriptors_inc() {} +static void live_descriptors_dec() {} +#else +#include + +static unsigned live_descriptors = 0; + +static void live_descriptors_inc() { live_descriptors += 1; } + +static void live_descriptors_dec() { + assert(live_descriptors > 0); + live_descriptors -= 1; +} + +void __wasilibc_assert_no_descriptor_leaks() { + if (!stdio_initialized) { + assert(live_descriptors == 0); + return; + } + + unsigned stdio_open = 0; + unsigned closed = 0; + for (size_t i = 0; i < global_table.len; i++) { + descriptor_table_item_t *table_entry = &global_table.entries[i]; + if (table_entry->occupied) { + if (i < 3) { + stdio_open++; + } else { + int rc = descriptor_table_remove(i); + assert(rc == 0); + closed++; + } + } + } + + if (live_descriptors == stdio_open) + return; + fprintf(stderr, "live_descriptors: %u\n", live_descriptors); + fprintf(stderr, "closed at end: %u\n", closed); + fprintf(stderr, "num stdio: %u\n", stdio_open); + fprintf(stderr, + "ERROR: detected a fd leak (live descriptors != num stdio)\n"); + __builtin_trap(); +} +#endif + int descriptor_table_insert(descriptor_table_entry_t entry) { + assert(entry.data->cnt == 0); + entry.data->cnt = 1; + live_descriptors_inc(); if (!stdio_initialized && init_stdio() < 0) goto error; - int fd = allocate(&global_table, entry); + int fd = table_allocate(&global_table, entry); if (fd < 0) goto error; return fd; error: - entry.vtable->free(entry.data); + descriptor_table_entry_dec(entry); return -1; } -descriptor_table_entry_t *descriptor_table_get_ref(int fd) { +int descriptor_table_get(int fd, descriptor_table_entry_t *entry) { if (!stdio_initialized && init_stdio() < 0) - return NULL; - return lookup(&global_table, fd); + return -1; + descriptor_table_entry_t *slot = table_lookup(&global_table, fd); + if (!slot) + return -1; + descriptor_table_entry_inc(*slot); + *entry = *slot; + return 0; } int descriptor_table_renumber(int fd, int newfd) { - descriptor_table_entry_t *fdentry = descriptor_table_get_ref(fd); + if (!stdio_initialized && init_stdio() < 0) + return -1; + descriptor_table_entry_t *fdentry = table_lookup(&global_table, fd); if (!fdentry) return -1; - descriptor_table_entry_t *newfdentry = descriptor_table_get_ref(newfd); + descriptor_table_entry_t *newfdentry = table_lookup(&global_table, newfd); if (!newfdentry) return -1; descriptor_table_entry_t temp = *fdentry; *fdentry = *newfdentry; *newfdentry = temp; - if (remove(&global_table, fd, &temp) < 0) + if (table_remove(&global_table, fd, &temp) < 0) return -1; - temp.vtable->free(temp.data); + descriptor_table_entry_dec(temp); return 0; } @@ -187,9 +245,9 @@ int descriptor_table_remove(int fd) { if (!stdio_initialized && init_stdio() < 0) return -1; descriptor_table_entry_t entry; - if (remove(&global_table, fd, &entry) < 0) + if (table_remove(&global_table, fd, &entry) < 0) return -1; - entry.vtable->free(entry.data); + descriptor_table_entry_dec(entry); return 0; } @@ -197,3 +255,11 @@ void descriptor_table_clear() { clear(&global_table); stdio_initialized = false; } + +void __wasilibc_descriptor_deallocate(descriptor_table_entry_t entry) { + assert(entry.data->cnt == 0); + int saved_errno = errno; + entry.vtable->free(entry.data); + errno = saved_errno; + live_descriptors_dec(); +} diff --git a/libc-bottom-half/sources/file.c b/libc-bottom-half/sources/file.c index a066eaaf2..4a82f6be8 100644 --- a/libc-bottom-half/sources/file.c +++ b/libc-bottom-half/sources/file.c @@ -10,6 +10,7 @@ #include "libc/sys/stat/stat_impl.h" typedef struct { + descriptor_refcnt_t refcnt; filesystem_own_descriptor_t file_handle; // Current position in stream, relative to the beginning of the // *file_handle*, measured in bytes @@ -350,6 +351,6 @@ int __wasilibc_add_file(filesystem_own_descriptor_t file_handle, int oflag) { descriptor_table_entry_t entry; entry.vtable = &file_vtable; - entry.data = file; + entry.data = &file->refcnt; return descriptor_table_insert(entry); } diff --git a/libc-bottom-half/sources/getsockpeername.c b/libc-bottom-half/sources/getsockpeername.c index 771806c97..46ffa593c 100644 --- a/libc-bottom-half/sources/getsockpeername.c +++ b/libc-bottom-half/sources/getsockpeername.c @@ -1,27 +1,30 @@ #include #include +#include #include int getsockname(int socket, struct sockaddr *__restrict addr, socklen_t *__restrict addrlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; - if (!entry->vtable->getsockname) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->getsockname) { errno = ENOTSOCK; return -1; } - return entry->vtable->getsockname(entry->data, addr, addrlen); + return entry.vtable->getsockname(entry.data, addr, addrlen); } int getpeername(int socket, struct sockaddr *__restrict addr, socklen_t *__restrict addrlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; - if (!entry->vtable->getpeername) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->getpeername) { errno = ENOTSOCK; return -1; } - return entry->vtable->getpeername(entry->data, addr, addrlen); + return entry.vtable->getpeername(entry.data, addr, addrlen); } diff --git a/libc-bottom-half/sources/isatty.c b/libc-bottom-half/sources/isatty.c index 2ac810012..e086ac209 100644 --- a/libc-bottom-half/sources/isatty.c +++ b/libc-bottom-half/sources/isatty.c @@ -7,6 +7,7 @@ #ifndef __wasip1__ #include +#include #include #endif @@ -30,15 +31,16 @@ int __isatty(int fd) { return 1; #elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor into an internal handle - descriptor_table_entry_t *entry = descriptor_table_get_ref(fd); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(fd, &entry) < 0) return 0; - if (!entry->vtable->isatty) { + defer descriptor_table_entry_dec(entry); + if (!entry.vtable->isatty) { errno = ENOTTY; return 0; } - return entry->vtable->isatty(entry->data); + return entry.vtable->isatty(entry.data); #else #error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/sources/listen.c b/libc-bottom-half/sources/listen.c index b33264a83..14c514a3d 100644 --- a/libc-bottom-half/sources/listen.c +++ b/libc-bottom-half/sources/listen.c @@ -1,11 +1,13 @@ #include #include +#include #include int listen(int socket, int backlog) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; + defer descriptor_table_entry_dec(entry); if (backlog < 0) { // POSIX: @@ -14,9 +16,9 @@ int listen(int socket, int backlog) { // > with a backlog argument value of 0. backlog = 0; } - if (!entry->vtable->listen) { + if (!entry.vtable->listen) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->listen(entry->data, backlog); + return entry.vtable->listen(entry.data, backlog); } diff --git a/libc-bottom-half/sources/recv.c b/libc-bottom-half/sources/recv.c index 16f58cf33..96951f9a5 100644 --- a/libc-bottom-half/sources/recv.c +++ b/libc-bottom-half/sources/recv.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -9,18 +10,19 @@ ssize_t recv(int socket, void *restrict buffer, size_t length, int flags) { ssize_t recvfrom(int socket, void *__restrict buffer, size_t length, int flags, struct sockaddr *__restrict addr, socklen_t *__restrict addrlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; + defer descriptor_table_entry_dec(entry); if (buffer == NULL) { errno = EINVAL; return -1; } - if (entry->vtable->recvfrom == NULL) { + if (entry.vtable->recvfrom == NULL) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->recvfrom(entry->data, buffer, length, flags, addr, - addrlen); + return entry.vtable->recvfrom(entry.data, buffer, length, flags, addr, + addrlen); } diff --git a/libc-bottom-half/sources/send.c b/libc-bottom-half/sources/send.c index 834ef67ca..1f309006c 100644 --- a/libc-bottom-half/sources/send.c +++ b/libc-bottom-half/sources/send.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,19 +9,19 @@ ssize_t send(int socket, const void *buffer, size_t length, int flags) { ssize_t sendto(int socket, const void *buffer, size_t length, int flags, const struct sockaddr *addr, socklen_t addrlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; + defer descriptor_table_entry_dec(entry); if (buffer == NULL) { errno = EINVAL; return -1; } - if (entry->vtable->sendto == NULL) { + if (entry.vtable->sendto == NULL) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->sendto(entry->data, buffer, length, flags, addr, - addrlen); + return entry.vtable->sendto(entry.data, buffer, length, flags, addr, addrlen); } diff --git a/libc-bottom-half/sources/shutdown.c b/libc-bottom-half/sources/shutdown.c index 735cc2b7f..233cb7938 100644 --- a/libc-bottom-half/sources/shutdown.c +++ b/libc-bottom-half/sources/shutdown.c @@ -1,13 +1,15 @@ #include +#include #include int shutdown(int socket, int how) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(socket); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(socket, &entry) < 0) return -1; - if (entry->vtable->shutdown == NULL) { + defer descriptor_table_entry_dec(entry); + if (entry.vtable->shutdown == NULL) { errno = EOPNOTSUPP; return -1; } - return entry->vtable->shutdown(entry->data, how); + return entry.vtable->shutdown(entry.data, how); } diff --git a/libc-bottom-half/sources/sockopt.c b/libc-bottom-half/sources/sockopt.c index 1673889d9..82f3992d2 100644 --- a/libc-bottom-half/sources/sockopt.c +++ b/libc-bottom-half/sources/sockopt.c @@ -1,12 +1,14 @@ #include #include +#include #include int getsockopt(int sockfd, int level, int optname, void *restrict optval, socklen_t *restrict optlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(sockfd); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(sockfd, &entry) < 0) return -1; + defer descriptor_table_entry_dec(entry); if (optval == NULL || optlen == NULL || *optlen < sizeof(int)) { // FYI, the protocol-specific implementations implicitly depend on these @@ -15,19 +17,20 @@ int getsockopt(int sockfd, int level, int optname, void *restrict optval, return -1; } - if (!entry->vtable->getsockopt) { + if (!entry.vtable->getsockopt) { errno = ENOPROTOOPT; return -1; } - return entry->vtable->getsockopt(entry->data, level, optname, optval, optlen); + return entry.vtable->getsockopt(entry.data, level, optname, optval, optlen); } int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(sockfd); - if (!entry) + descriptor_table_entry_t entry; + if (descriptor_table_get(sockfd, &entry) < 0) return -1; + defer descriptor_table_entry_dec(entry); if (optval == NULL || optlen < sizeof(int)) { // FYI, the protocol-specific implementations implicitly depend on these @@ -36,10 +39,10 @@ int setsockopt(int sockfd, int level, int optname, const void *optval, return -1; } - if (!entry->vtable->setsockopt) { + if (!entry.vtable->setsockopt) { errno = ENOPROTOOPT; return -1; } - return entry->vtable->setsockopt(entry->data, level, optname, optval, optlen); + return entry.vtable->setsockopt(entry.data, level, optname, optval, optlen); } diff --git a/libc-bottom-half/sources/tcp.c b/libc-bottom-half/sources/tcp.c index 805533758..dcff14ecf 100644 --- a/libc-bottom-half/sources/tcp.c +++ b/libc-bottom-half/sources/tcp.c @@ -83,7 +83,7 @@ static int tcp_add(sockets_own_tcp_socket_t socket, descriptor_table_entry_t entry; entry.vtable = &tcp_vtable; - entry.data = tcp; + entry.data = &tcp->refcnt; if (out) *out = tcp; return descriptor_table_insert(entry); diff --git a/libc-bottom-half/sources/udp.c b/libc-bottom-half/sources/udp.c index 08692f82f..91a4745fd 100644 --- a/libc-bottom-half/sources/udp.c +++ b/libc-bottom-half/sources/udp.c @@ -118,6 +118,7 @@ typedef struct { } udp_socket_state_t; typedef struct { + descriptor_refcnt_t refcnt; sockets_own_udp_socket_t socket; #ifdef __wasip2__ // Lazily initialized as-needed pollable. @@ -146,7 +147,7 @@ int __wasilibc_add_udp_socket(sockets_own_udp_socket_t socket, descriptor_table_entry_t entry; entry.vtable = &udp_vtable; - entry.data = udp; + entry.data = &udp->refcnt; return descriptor_table_insert(entry); } diff --git a/libc-bottom-half/sources/wasip2_stdio.c b/libc-bottom-half/sources/wasip2_stdio.c index 47e9044c2..761c3d07c 100644 --- a/libc-bottom-half/sources/wasip2_stdio.c +++ b/libc-bottom-half/sources/wasip2_stdio.c @@ -9,6 +9,7 @@ #ifdef __wasip2__ typedef struct { + descriptor_refcnt_t refcnt; int fd; // Lazily initialized streams. Depending on `fd` these may never be // initialized, for example stdin can never have an output stream. @@ -138,7 +139,7 @@ static int stdio_add(int fd) { descriptor_table_entry_t entry; entry.vtable = &stdio_vtable; - entry.data = stdio; + entry.data = &stdio->refcnt; return descriptor_table_insert(entry); } diff --git a/libc-bottom-half/sources/wasip3_stdio.c b/libc-bottom-half/sources/wasip3_stdio.c index 031972281..a2f16cba9 100644 --- a/libc-bottom-half/sources/wasip3_stdio.c +++ b/libc-bottom-half/sources/wasip3_stdio.c @@ -16,6 +16,7 @@ typedef stdout_future_result_void_error_code_t (*stdout_stream_func_t)( stdin_stream_u8_t data); typedef struct { + descriptor_refcnt_t refcnt; wasip3_io_state_t input; stdin_future_result_void_error_code_t input_result; // tristate: zero=unknown, valid handle=yes, -1=no @@ -23,6 +24,7 @@ typedef struct { } stdin3_t; typedef struct { + descriptor_refcnt_t refcnt; // contains stream, result storage and result subtask stdout_future_result_void_error_code_t result; wasip3_io_state_t output; @@ -201,7 +203,7 @@ static int stdio_add_input() { } descriptor_table_entry_t entry; entry.vtable = &stdin3_vtable; - entry.data = stdio; + entry.data = &stdio->refcnt; return descriptor_table_insert(entry); } @@ -218,7 +220,7 @@ static int stdio3_add_output( descriptor_table_entry_t entry; entry.vtable = &stdout3_vtable; - entry.data = stdio; + entry.data = &stdio->refcnt; return descriptor_table_insert(entry); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 92ae176a6..4823bf367 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -457,12 +457,26 @@ if (NOT (WASI STREQUAL "p1")) add_wasilibc_test(clear_fds.c FS) endif() -if (CMAKE_C_COMPILER_VERSION VERSION_GREATER 20.0) - # TODO: can't build a shared library with setjmp just yet because `wasm-ld` - # doesn't work well with tags exported from shared libraries. Probably related - # to llvm-project/llvm#188367 and/or llvm/llvm-project#188120 - add_wasilibc_test(setjmp.c SETJMP NOSHARED) - expect_fail_under_v8(setjmp.wasm) +if ( + # Setjmp currently requires llvm 20+ for the support necessary in wasi-libc. + CMAKE_C_COMPILER_VERSION VERSION_GREATER 20.0 + # LTO + LLVM 22 is buggy + AND ( + (NOT LTO) OR + (CMAKE_C_COMPILER_VERSION VERSION_LESS 22.0) OR + (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 23.0) + ) + # Currently not setup to run this test + AND NOT TEST_WITH_V8 +) + # LLVM 22-and-prior can't build a shared library with setjmp just yet because + # `wasm-ld` didn't work well with tags exported from shared libraries. + # Supported with LLVM 23-and-later + set(noshared NOSHARED) + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 23.0) + set(noshared) + endif() + add_wasilibc_test(setjmp.c SETJMP ${noshared}) endif() # This test works for single-threaded targets too