Skip to content

Commit 4d9cd9d

Browse files
authored
clear descriptor table in __wasilibc_reset_preopens (#776)
The original intention of `__wasilibc_reset_preopens` was to clear the stdio state as the last step of Wizer-style pre-initialization since none of the handles would be valid when the snapshot is resumed. In earlier versions of `wasi-libc`, which didn't have native p2 or p3 support for stdio or file I/O, resetting the WASIp1 state was sufficient; the `wasi_snapshot_preview1` adapter took care of emulating p1 in terms of p2. However, now that `wasi-libc` has native support for p2 and p3, we need to also reset the descriptor table. At this point, the name `__wasilibc_reset_preopens` is a bit misleading for p2+ since we're clearing more than just the preopens, but changing the name would break tools which use it, so probably not worth the trouble.
1 parent f817556 commit 4d9cd9d

8 files changed

Lines changed: 78 additions & 2 deletions

File tree

expected/wasm32-wasip2/defined-symbols.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ ctanhl
522522
ctanl
523523
ctime
524524
ctime_r
525+
descriptor_table_clear
525526
descriptor_table_get_ref
526527
descriptor_table_insert
527528
descriptor_table_remove
@@ -1569,4 +1570,4 @@ y0f
15691570
y1
15701571
y1f
15711572
yn
1572-
ynf
1573+
ynf

expected/wasm32-wasip3/defined-symbols.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ ctanhl
523523
ctanl
524524
ctime
525525
ctime_r
526+
descriptor_table_clear
526527
descriptor_table_get_ref
527528
descriptor_table_insert
528529
descriptor_table_remove
@@ -1596,4 +1597,4 @@ y0f
15961597
y1
15971598
y1f
15981599
yn
1599-
ynf
1600+
ynf

libc-bottom-half/headers/private/wasi/descriptor_table.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ int descriptor_table_remove(int fd);
239239
/// errno on failure.
240240
int descriptor_table_renumber(int fd, int newfd);
241241

242+
/// Removes all file descriptors from the table, running their destructors.
243+
void descriptor_table_clear();
244+
242245
#endif // __wasip1__
243246

244247
#endif // DESCRIPTOR_TABLE_H

libc-bottom-half/headers/public/wasi/libc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ struct timespec;
1919
/// afterward, you should call this before doing so.
2020
void __wasilibc_populate_preopens(void);
2121

22+
/// Clear the set of preopens (and, on p2+, any other open file descriptors).
23+
///
24+
/// This is appropriate to call just prior to snapshotting the guest state so
25+
/// that it can be resumed on another runtime, in which case any
26+
/// previously-opened handles will no longer be valid.
27+
void __wasilibc_reset_preopens();
28+
2229
#ifndef __wasip2__
2330
/// Register the given pre-opened file descriptor under the given path.
2431
int __wasilibc_register_preopened_fd(int fd, const char *prefix);

libc-bottom-half/sources/descriptor_table.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,22 @@ static int remove(descriptor_table_t *table, int fd,
125125
return 0;
126126
}
127127

128+
static void clear(descriptor_table_t *table) {
129+
for (size_t i = 0; i < table->len; ++i) {
130+
descriptor_table_item_t *table_entry = &table->entries[i];
131+
if (table_entry->occupied) {
132+
descriptor_table_entry_t entry = table_entry->entry;
133+
entry.vtable->free(entry.data);
134+
}
135+
}
136+
if (table->entries)
137+
free(table->entries);
138+
table->entries = NULL;
139+
table->next = 0;
140+
table->len = 0;
141+
table->cap = 0;
142+
}
143+
128144
static bool stdio_initialized = false;
129145

130146
static int init_stdio() {
@@ -176,3 +192,8 @@ int descriptor_table_remove(int fd) {
176192
entry.vtable->free(entry.data);
177193
return 0;
178194
}
195+
196+
void descriptor_table_clear() {
197+
clear(&global_table);
198+
stdio_initialized = false;
199+
}

libc-bottom-half/sources/preopens.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,5 +418,9 @@ void __wasilibc_reset_preopens(void) {
418418

419419
assert_invariants();
420420

421+
#ifndef __wasip1__
422+
descriptor_table_clear();
423+
#endif
424+
421425
UNLOCK(lock);
422426
}

test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,12 @@ add_wasilibc_test(sleep.c)
330330
add_wasilibc_test(write.c FS)
331331
add_wasilibc_test(wasi-defines.c)
332332

333+
# This tests the behavior of the WASIp2+ descriptor table and won't succeed on
334+
# p1:
335+
if (NOT (WASI STREQUAL "p1"))
336+
add_wasilibc_test(clear_fds.c FS)
337+
endif()
338+
333339
if (CMAKE_C_COMPILER_VERSION VERSION_GREATER 20.0)
334340
add_wasilibc_test(setjmp.c SETJMP)
335341
set_tests_properties(setjmp.wasm PROPERTIES LABELS v8fail)

test/src/clear_fds.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "test.h"
2+
#include <errno.h>
3+
#include <fcntl.h>
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <unistd.h>
8+
#include <wasi/libc.h>
9+
10+
#define TEST(c) \
11+
do { \
12+
errno = 0; \
13+
if (!(c)) \
14+
t_error("%s failed (errno = %d)\n", #c, errno); \
15+
} while (0)
16+
17+
int main(void) {
18+
char tmp[] = "testsuite-XXXXXX";
19+
int fd;
20+
TEST((fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0600)) > 2);
21+
TEST(write(fd, "hello", 6) == 6);
22+
TEST(write(2, "test stderr\n", 12) == 12);
23+
// This should clear the entire descriptor table, closing all descriptors
24+
// (e.g. as the last step of a Wizer-style pre-init function).
25+
__wasilibc_reset_preopens();
26+
if (!(write(fd, "hello", 6) == -1 && errno == EBADF))
27+
t_error("fd should have been closed by `__wasilibc_reset_preopens`");
28+
// stdio handles will be lazily reinitialized, so should still work:
29+
TEST(write(2, "test stderr\n", 12) == 12);
30+
if (fd > 2)
31+
TEST(unlink(tmp) != -1);
32+
return t_status;
33+
}

0 commit comments

Comments
 (0)