diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ab1ca3..61fa69f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ endif() set(uvwasi_sources src/clocks.c src/fd_table.c + src/serdes.c src/uv_mapping.c src/uvwasi.c src/wasi_rights.c diff --git a/include/serdes.h b/include/serdes.h new file mode 100644 index 0000000..d7ff2f8 --- /dev/null +++ b/include/serdes.h @@ -0,0 +1,131 @@ +#ifndef __UVWASI_SERDES_H__ +#define __UVWASI_SERDES_H__ + +#include "wasi_types.h" + +/* + Basic uint{8,16,32,64}_t read/write functions. +*/ + +#define BASIC_TYPE_(name, type) \ + void uvwasi_serdes_write_##name(void* ptr, size_t offset, type value); \ + type uvwasi_serdes_read_##name(const void* ptr, size_t offset); \ + +#define BASIC_TYPE(type) BASIC_TYPE_(type, type) +#define BASIC_TYPE_UVWASI(type) BASIC_TYPE_(type, uvwasi_##type) + +#define UVWASI_SERDES_SIZE_uint8_t sizeof(uint8_t) +BASIC_TYPE(uint8_t) +#define UVWASI_SERDES_SIZE_uint16_t sizeof(uint16_t) +BASIC_TYPE(uint16_t) +#define UVWASI_SERDES_SIZE_uint32_t sizeof(uint32_t) +BASIC_TYPE(uint32_t) +#define UVWASI_SERDES_SIZE_uint64_t sizeof(uint64_t) +BASIC_TYPE(uint64_t) + +#define UVWASI_SERDES_SIZE_advice_t sizeof(uvwasi_advice_t) +BASIC_TYPE_UVWASI(advice_t) +#define UVWASI_SERDES_SIZE_clockid_t sizeof(uvwasi_clockid_t) +BASIC_TYPE_UVWASI(clockid_t) +#define UVWASI_SERDES_SIZE_device_t sizeof(uvwasi_device_t) +BASIC_TYPE_UVWASI(device_t) +#define UVWASI_SERDES_SIZE_dircookie_t sizeof(uvwasi_dircookie_t) +BASIC_TYPE_UVWASI(dircookie_t) +#define UVWASI_SERDES_SIZE_eventrwflags_t sizeof(uvwasi_eventrwflags_t) +BASIC_TYPE_UVWASI(eventrwflags_t) +#define UVWASI_SERDES_SIZE_eventtype_t sizeof(uvwasi_eventtype_t) +BASIC_TYPE_UVWASI(eventtype_t) +#define UVWASI_SERDES_SIZE_exitcode_t sizeof(uvwasi_exitcode_t) +BASIC_TYPE_UVWASI(exitcode_t) +#define UVWASI_SERDES_SIZE_fd_t sizeof(uvwasi_fd_t) +BASIC_TYPE_UVWASI(fd_t) +#define UVWASI_SERDES_SIZE_fdflags_t sizeof(uvwasi_fdflags_t) +BASIC_TYPE_UVWASI(fdflags_t) +#define UVWASI_SERDES_SIZE_filesize_t sizeof(uvwasi_filesize_t) +BASIC_TYPE_UVWASI(filesize_t) +#define UVWASI_SERDES_SIZE_fstflags_t sizeof(uvwasi_fstflags_t) +BASIC_TYPE_UVWASI(fstflags_t) +#define UVWASI_SERDES_SIZE_inode_t sizeof(uvwasi_inode_t) +BASIC_TYPE_UVWASI(inode_t) +#define UVWASI_SERDES_SIZE_linkcount_t sizeof(uvwasi_linkcount_t) +BASIC_TYPE_UVWASI(linkcount_t) +#define UVWASI_SERDES_SIZE_lookupflags_t sizeof(uvwasi_lookupflags_t) +BASIC_TYPE_UVWASI(lookupflags_t) +#define UVWASI_SERDES_SIZE_oflags_t sizeof(uvwasi_oflags_t) +BASIC_TYPE_UVWASI(oflags_t) +#define UVWASI_SERDES_SIZE_preopentype_t sizeof(uvwasi_preopentype_t) +BASIC_TYPE_UVWASI(preopentype_t) +#define UVWASI_SERDES_SIZE_riflags_t sizeof(uvwasi_riflags_t) +BASIC_TYPE_UVWASI(riflags_t) +#define UVWASI_SERDES_SIZE_rights_t sizeof(uvwasi_rights_t) +BASIC_TYPE_UVWASI(rights_t) +#define UVWASI_SERDES_SIZE_roflags_t sizeof(uvwasi_roflags_t) +BASIC_TYPE_UVWASI(roflags_t) +#define UVWASI_SERDES_SIZE_sdflags_t sizeof(uvwasi_sdflags_t) +BASIC_TYPE_UVWASI(sdflags_t) +#define UVWASI_SERDES_SIZE_siflags_t sizeof(uvwasi_siflags_t) +BASIC_TYPE_UVWASI(siflags_t) +#define UVWASI_SERDES_SIZE_inode_t sizeof(uvwasi_inode_t) +BASIC_TYPE_UVWASI(inode_t) +#define UVWASIS_SERDES_SIZE_signal_t sizeof(uvwasi_signal_t) +BASIC_TYPE_UVWASI(signal_t) +#define UVWASIS_SERDES_SIZE_subclockflags_t sizeof(uvwasi_subclockflags_t) +BASIC_TYPE_UVWASI(subclockflags_t) +#define UVWASIS_SERDES_SIZE_timestamp_t sizeof(uvwasi_timestamp_t) +BASIC_TYPE_UVWASI(timestamp_t) +#define UVWASIS_SERDES_SIZE_userdata_t sizeof(uvwasi_userdata_t) +BASIC_TYPE_UVWASI(userdata_t) +#define UVWASIS_SERDES_SIZE_whence_t sizeof(uvwasi_whence_t) +BASIC_TYPE_UVWASI(whence_t) + +#undef BASIC_TYPE_UVWASI +#undef BASIC_TYPE +#undef BASIC_TYPE_ + +/* + WASI structure read/write functions. +*/ + +#define STRUCT(name) \ + void uvwasi_serdes_write_##name(void* ptr, \ + size_t offset, \ + const uvwasi_##name* value); \ + void uvwasi_serdes_read_##name(const void* ptr, \ + size_t offset, \ + uvwasi_##name* value); \ + +#define UVWASI_SERDES_SIZE_fdstat_t 24 +STRUCT(fdstat_t) + +#define UVWASI_SERDES_SIZE_filestat_t 64 +STRUCT(filestat_t) + +#define UVWASI_SERDES_SIZE_prestat_t 8 +STRUCT(prestat_t) + +#define UVWASI_SERDES_SIZE_event_t 32 +STRUCT(event_t) + +#define UVWASI_SERDES_SIZE_subscription_t 48 +STRUCT(subscription_t) + +#undef STRUCT + +/* + Helper macros for bound checking. +*/ + +#define UVWASI_SERDES_CHECK_BOUNDS(offset, end, type) \ + ((offset) >= 0 && \ + (end) > (offset) && \ + (UVWASI_SERDES_SIZE_##type <= (end) - (offset))) \ + +#define UVWASI_SERDES_CHECK_ARRAY_BOUNDS(offset, end, type, count) \ + ((offset) >= 0 && \ + (end) > (offset) && \ + (count) >= 0 && \ + ((count) * UVWASI_SERDES_SIZE_##type) / UVWASI_SERDES_SIZE_##type == \ + (count) && \ + ((count) * UVWASI_SERDES_SIZE_##type <= (end) - (offset))) \ + +#endif /* __UVWASI_SERDES_H__ */ diff --git a/include/uvwasi.h b/include/uvwasi.h index 9ca3045..414a460 100644 --- a/include/uvwasi.h +++ b/include/uvwasi.h @@ -8,6 +8,7 @@ extern "C" { #include "uv.h" #include "wasi_types.h" #include "fd_table.h" +#include "serdes.h" #define UVWASI_VERSION_MAJOR 0 #define UVWASI_VERSION_MINOR 0 diff --git a/src/serdes.c b/src/serdes.c new file mode 100644 index 0000000..0904fea --- /dev/null +++ b/src/serdes.c @@ -0,0 +1,170 @@ +#include "uvwasi.h" + +inline void uvwasi_serdes_write_uint64_t(void* ptr, + size_t offset, + uint64_t value) { + uvwasi_serdes_write_uint32_t(ptr, offset, (uint32_t) value); + uvwasi_serdes_write_uint32_t(ptr, offset + 4, value >> 32); +} + +inline void uvwasi_serdes_write_uint32_t(void* ptr, + size_t offset, + uint32_t value) { + uvwasi_serdes_write_uint16_t(ptr, offset, (uint16_t) value); + uvwasi_serdes_write_uint16_t(ptr, offset + 2, value >> 16); +} + +inline void uvwasi_serdes_write_uint16_t(void* ptr, + size_t offset, + uint16_t value) { + uvwasi_serdes_write_uint8_t(ptr, offset, (uint8_t) value); + uvwasi_serdes_write_uint8_t(ptr, offset + 1, value >> 8); +} + +inline void uvwasi_serdes_write_uint8_t(void* ptr, + size_t offset, + uint8_t value) { + ((uint8_t*) ptr)[offset] = value; +} + +inline uint64_t uvwasi_serdes_read_uint64_t(const void* ptr, size_t offset) { + uint64_t low = uvwasi_serdes_read_uint32_t(ptr, offset); + uint64_t high = uvwasi_serdes_read_uint32_t(ptr, offset + 4); + return low | (high << 32); +} + +inline uint32_t uvwasi_serdes_read_uint32_t(const void* ptr, size_t offset) { + uint32_t low = uvwasi_serdes_read_uint16_t(ptr, offset); + uint32_t high = uvwasi_serdes_read_uint16_t(ptr, offset + 2); + return low | (high << 16); +} + +inline uint16_t uvwasi_serdes_read_uint16_t(const void* ptr, size_t offset) { + uint16_t low = uvwasi_serdes_read_uint8_t(ptr, offset); + uint16_t high = uvwasi_serdes_read_uint8_t(ptr, offset + 1); + return low | (high << 8); +} + +inline uint8_t uvwasi_serdes_read_uint8_t(const void* ptr, size_t offset) { + return ((const uint8_t*) ptr)[offset]; +} + +#define TYPE_SWITCH switch (value->type) + +#define ALL_TYPES(STRUCT, FIELD, ALIAS) \ + \ + ALIAS(advice_t, uint8_t) \ + ALIAS(clockid_t, uint32_t) \ + ALIAS(device_t, uint64_t) \ + ALIAS(dircookie_t, uint64_t) \ + ALIAS(errno_t, uint16_t) \ + ALIAS(eventrwflags_t, uint16_t) \ + ALIAS(eventtype_t, uint8_t) \ + ALIAS(exitcode_t, uint32_t) \ + ALIAS(fd_t, uint32_t) \ + ALIAS(fdflags_t, uint16_t) \ + ALIAS(filesize_t, uint64_t) \ + ALIAS(filetype_t, uint8_t) \ + ALIAS(fstflags_t, uint16_t) \ + ALIAS(inode_t, uint64_t) \ + ALIAS(linkcount_t, uint64_t) \ + ALIAS(lookupflags_t, uint32_t) \ + ALIAS(oflags_t, uint16_t) \ + ALIAS(preopentype_t, uint8_t) \ + ALIAS(riflags_t, uint16_t) \ + ALIAS(rights_t, uint64_t) \ + ALIAS(roflags_t, uint16_t) \ + ALIAS(sdflags_t, uint8_t) \ + ALIAS(siflags_t, uint16_t) \ + ALIAS(signal_t, uint8_t) \ + ALIAS(subclockflags_t, uint16_t) \ + ALIAS(timestamp_t, uint64_t) \ + ALIAS(userdata_t, uint64_t) \ + ALIAS(whence_t, uint8_t) \ + \ + STRUCT(fdstat_t) { \ + FIELD( 0, filetype_t, fs_filetype); \ + FIELD( 2, fdflags_t, fs_flags); \ + FIELD( 8, rights_t, fs_rights_base); \ + FIELD(16, rights_t, fs_rights_inheriting); \ + } \ + \ + STRUCT(filestat_t) { \ + FIELD( 0, device_t, st_dev); \ + FIELD( 8, inode_t, st_ino); \ + FIELD(16, filetype_t, st_filetype); \ + FIELD(24, linkcount_t, st_nlink); \ + FIELD(32, filesize_t, st_size); \ + FIELD(40, timestamp_t, st_atim); \ + FIELD(48, timestamp_t, st_mtim); \ + FIELD(56, timestamp_t, st_ctim); \ + } \ + \ + STRUCT(prestat_t) { \ + FIELD(0, preopentype_t, pr_type); \ + FIELD(4, uint32_t, u.dir.pr_name_len); \ + } \ + \ + STRUCT(event_t) { \ + FIELD( 0, userdata_t, userdata); \ + FIELD( 8, errno_t, error); \ + FIELD(10, eventtype_t, type); \ + TYPE_SWITCH { \ + case UVWASI_EVENTTYPE_FD_READ: \ + case UVWASI_EVENTTYPE_FD_WRITE: \ + FIELD(16, filesize_t, u.fd_readwrite.nbytes); \ + FIELD(24, eventrwflags_t, u.fd_readwrite.flags); \ + } \ + } \ + \ + STRUCT(subscription_t) { \ + FIELD(0, userdata_t, userdata); \ + FIELD(8, eventtype_t, type); \ + TYPE_SWITCH { \ + case UVWASI_EVENTTYPE_CLOCK: \ + FIELD(16, clockid_t, u.clock.clock_id); \ + FIELD(24, timestamp_t, u.clock.timeout); \ + FIELD(32, timestamp_t, u.clock.precision); \ + FIELD(40, subclockflags_t, u.clock.flags); \ + break; \ + case UVWASI_EVENTTYPE_FD_READ: \ + case UVWASI_EVENTTYPE_FD_WRITE: \ + FIELD(16, fd_t, u.fd_readwrite.fd); \ + } \ + } \ + +#define WRITE_STRUCT(name) \ + void uvwasi_serdes_write_##name(void* ptr, \ + size_t offset, \ + const uvwasi_##name* value) \ + +#define READ_STRUCT(name) \ + void uvwasi_serdes_read_##name(const void* ptr, \ + size_t offset, \ + uvwasi_##name* value) \ + +#define WRITE_FIELD(field_offset, type, field) \ + do { \ + uvwasi_serdes_write_##type(ptr, offset + field_offset, value->field); \ + } while (0) \ + +#define READ_FIELD(field_offset, type, field) \ + do { \ + value->field = uvwasi_serdes_read_##type(ptr, offset + field_offset); \ + } while (0) \ + +#define WRITE_ALIAS(new_name, old_name) \ + void uvwasi_serdes_write_##new_name(void* ptr, \ + size_t offset, \ + uvwasi_##new_name value) { \ + uvwasi_serdes_write_##old_name(ptr, offset, value); \ + } \ + +#define READ_ALIAS(new_name, old_name) \ + uvwasi_##new_name uvwasi_serdes_read_##new_name(const void* ptr, \ + size_t offset) { \ + return uvwasi_serdes_read_##old_name(ptr, offset); \ + } \ + +ALL_TYPES(WRITE_STRUCT, WRITE_FIELD, WRITE_ALIAS) +ALL_TYPES(READ_STRUCT, READ_FIELD, READ_ALIAS) diff --git a/test/test-serdes.c b/test/test-serdes.c new file mode 100644 index 0000000..cece8db --- /dev/null +++ b/test/test-serdes.c @@ -0,0 +1,287 @@ +#include +#include +#include +#include "uvwasi.h" + +void test_bound_checks(void); +void test_basic_types(void); +void test_fdstat_t(void); +void test_filestat_t(void); +void test_prestat_t(void); +void test_event_t(void); +void test_subscription_t(void); + +int main(void) { + test_bound_checks(); + test_basic_types(); + test_fdstat_t(); + test_filestat_t(); + test_prestat_t(); + test_event_t(); + test_subscription_t(); + return 0; +} + +/* First 16 digits of PI, used to make sure that the algorithm doesn't overwrite + anything it shouldn't. +*/ +const char canary[16] = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3 }; + +#define ADD_CANARIES(type) ((UVWASI_SERDES_SIZE_##type) + 2 * sizeof(canary)) + +/* Writes the canary to the beginning and end of the buffer. */ +void use_canaries(char* ptr, size_t size) { + assert(size >= 2 * sizeof(canary)); + memcpy(ptr, canary, sizeof(canary)); + memcpy(ptr + size - sizeof(canary), canary, sizeof(canary)); +} + +/* Checks that the canaries at the beginning and end of the buffer are intact. */ +void check_canaries(const char* ptr, size_t size) { + assert(size >= 2 * sizeof(canary)); + assert(memcmp(ptr, canary, sizeof(canary)) == 0); + assert(memcmp(ptr + size - sizeof(canary), canary, sizeof(canary)) == 0); +} + +void test_bound_checks(void) { + /* Regardless of the type, the macro should catch negative offsets and sizes. */ + assert(!UVWASI_SERDES_CHECK_BOUNDS(-500, 1000, uint8_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(-500, -100, uint16_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(5000, 1000, uint32_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(-500, 1000, uint64_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(-500, -100, event_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(5000, 1000, fdstat_t)); + assert(!UVWASI_SERDES_CHECK_ARRAY_BOUNDS(0, 1000, filestat_t, -1)); + /* This causes an integer overflow, which should be detected correctly. */ + assert(!UVWASI_SERDES_CHECK_ARRAY_BOUNDS(0, 0xffffffffffffffffllu, + subscription_t, + 0xffffffffffffffffllu)); + + assert(UVWASI_SERDES_CHECK_BOUNDS(19, 20, uint8_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(20, 20, uint8_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(18, 20, uint16_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(19, 20, uint16_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(16, 20, uint32_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(17, 20, uint32_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(12, 20, uint64_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(13, 20, uint64_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(0, 24, fdstat_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(1, 24, fdstat_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(0, 64, filestat_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(1, 64, filestat_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(0, 8, prestat_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(1, 8, prestat_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(0, 32, event_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(1, 32, event_t)); + assert(UVWASI_SERDES_CHECK_BOUNDS(0, 48, subscription_t)); + assert(!UVWASI_SERDES_CHECK_BOUNDS(1, 48, subscription_t)); + + assert(UVWASI_SERDES_CHECK_ARRAY_BOUNDS(0, 480, subscription_t, 10)); + assert(!UVWASI_SERDES_CHECK_ARRAY_BOUNDS(1, 480, subscription_t, 10)); + assert(UVWASI_SERDES_CHECK_ARRAY_BOUNDS(0, 8000, inode_t, 1000)); + assert(!UVWASI_SERDES_CHECK_ARRAY_BOUNDS(1, 8000, inode_t, 1000)); +} + +void test_basic_types(void) { + { + char buf[ADD_CANARIES(uint8_t)]; + use_canaries(buf, sizeof(buf)); + uvwasi_serdes_write_uint8_t(buf, sizeof(canary), 0xabu); + check_canaries(buf, sizeof(buf)); + assert(uvwasi_serdes_read_uint8_t(buf, sizeof(canary)) == 0xabu); + } + { + char buf[ADD_CANARIES(uint16_t)]; + use_canaries(buf, sizeof(buf)); + uvwasi_serdes_write_uint16_t(buf, sizeof(canary), 0xabcdu); + check_canaries(buf, sizeof(buf)); + assert(uvwasi_serdes_read_uint16_t(buf, sizeof(canary)) == 0xabcdu); + } + { + char buf[ADD_CANARIES(uint32_t)]; + use_canaries(buf, sizeof(buf)); + uvwasi_serdes_write_uint32_t(buf, sizeof(canary), 0xabcdef01u); + check_canaries(buf, sizeof(buf)); + assert(uvwasi_serdes_read_uint32_t(buf, sizeof(canary)) == 0xabcdef01u); + } + { + char buf[ADD_CANARIES(uint64_t)]; + use_canaries(buf, sizeof(buf)); + uvwasi_serdes_write_uint64_t(buf, sizeof(canary), 0xabcdef0123llu); + check_canaries(buf, sizeof(buf)); + assert(uvwasi_serdes_read_uint64_t(buf, sizeof(canary)) == 0xabcdef0123llu); + } +} + +void test_fdstat_t(void) { + uvwasi_fdstat_t stat = { + .fs_filetype = UVWASI_FILETYPE_DIRECTORY, + .fs_flags = UVWASI_FDFLAG_APPEND, + .fs_rights_base = UVWASI_RIGHT_FD_WRITE, + .fs_rights_inheriting = UVWASI_RIGHT_FD_WRITE + }; + + char data[ADD_CANARIES(fdstat_t)] = { 0 }; + use_canaries(data, sizeof(data)); + uvwasi_serdes_write_fdstat_t(data, sizeof(canary), &stat); + check_canaries(data, sizeof(data)); + /* TODO(tniessen): Check result of serialization. */ + + uvwasi_fdstat_t deserialized; + uvwasi_serdes_read_fdstat_t(data, sizeof(canary), &deserialized); + assert(deserialized.fs_filetype == stat.fs_filetype); + assert(deserialized.fs_flags == stat.fs_flags); + assert(deserialized.fs_rights_base == stat.fs_rights_base); + assert(deserialized.fs_rights_inheriting == stat.fs_rights_inheriting); +} + +void test_filestat_t(void) { + uvwasi_filestat_t stat = { + .st_dev = 0x1234567812345678llu, + .st_ino = 0x8765432187654321llu, + .st_filetype = UVWASI_FILETYPE_REGULAR_FILE, + .st_nlink = 0x1000000000000001llu, + .st_size = 0x9999999999999999llu, + .st_atim = 0x8888888888888888llu, + .st_mtim = 0x7777777777777777llu, + .st_ctim = 0x6666666666666666llu + }; + + char data[ADD_CANARIES(filestat_t)] = { 0 }; + use_canaries(data, sizeof(data)); + uvwasi_serdes_write_filestat_t(data, sizeof(canary), &stat); + check_canaries(data, sizeof(data)); + /* TODO(tniessen): Check result of serialization. */ + + uvwasi_filestat_t deserialized; + uvwasi_serdes_read_filestat_t(data, sizeof(canary), &deserialized); + assert(deserialized.st_dev == stat.st_dev); + assert(deserialized.st_ino == stat.st_ino); + assert(deserialized.st_filetype == stat.st_filetype); + assert(deserialized.st_nlink == stat.st_nlink); + assert(deserialized.st_size == stat.st_size); + assert(deserialized.st_atim == stat.st_atim); + assert(deserialized.st_mtim == stat.st_mtim); + assert(deserialized.st_ctim == stat.st_ctim); +} + +void test_prestat_t(void) { + uvwasi_prestat_t stat = { + .pr_type = UVWASI_PREOPENTYPE_DIR, + .u = { + .dir = { + .pr_name_len = 100 + } + } + }; + + char data[ADD_CANARIES(prestat_t)] = { 0 }; + use_canaries(data, sizeof(data)); + uvwasi_serdes_write_prestat_t(data, sizeof(canary), &stat); + check_canaries(data, sizeof(data)); + /* TODO(tniessen): Check result of serialization. */ + + uvwasi_prestat_t deserialized; + uvwasi_serdes_read_prestat_t(data, sizeof(canary), &deserialized); + assert(deserialized.pr_type == stat.pr_type); + assert(deserialized.u.dir.pr_name_len == stat.u.dir.pr_name_len); +} + +void test_event_t(void) { + uvwasi_event_t event = { + .userdata = 0xabcdabcdabcdabcdllu, + .error = 0xabcd, + .type = UVWASI_EVENTTYPE_CLOCK + }; + + char data[ADD_CANARIES(event_t)] = { 0 }; + use_canaries(data, sizeof(data)); + uvwasi_serdes_write_event_t(data, sizeof(canary), &event); + check_canaries(data, sizeof(data)); + /* TODO(tniessen): Check result of serialization. */ + + uvwasi_event_t deserialized = { 0 }; + uvwasi_serdes_read_event_t(data, sizeof(canary), &deserialized); + assert(deserialized.userdata == event.userdata); + assert(deserialized.error == event.error); + assert(deserialized.type == event.type); + assert(deserialized.u.fd_readwrite.nbytes == 0); + assert(deserialized.u.fd_readwrite.flags == 0); + + event.type = UVWASI_EVENTTYPE_FD_READ; + event.u.fd_readwrite.nbytes = 1000; + event.u.fd_readwrite.flags = UVWASI_EVENT_FD_READWRITE_HANGUP; + uvwasi_serdes_write_event_t(data, sizeof(canary), &event); + /* TODO(tniessen): Check result of serialization. */ + + memset(&deserialized, 0, sizeof(deserialized)); + uvwasi_serdes_read_event_t(data, sizeof(canary), &deserialized); + assert(deserialized.userdata == event.userdata); + assert(deserialized.error == event.error); + assert(deserialized.type == event.type); + assert(deserialized.u.fd_readwrite.nbytes == event.u.fd_readwrite.nbytes); + assert(deserialized.u.fd_readwrite.flags == event.u.fd_readwrite.flags); + + event.type = UVWASI_EVENTTYPE_FD_WRITE; + uvwasi_serdes_write_event_t(data, sizeof(canary), &event); + /* TODO(tniessen): Check result of serialization. */ + + memset(&deserialized, 0, sizeof(deserialized)); + uvwasi_serdes_read_event_t(data, sizeof(canary), &deserialized); + assert(deserialized.userdata == event.userdata); + assert(deserialized.error == event.error); + assert(deserialized.type == event.type); + assert(deserialized.u.fd_readwrite.nbytes == event.u.fd_readwrite.nbytes); + assert(deserialized.u.fd_readwrite.flags == event.u.fd_readwrite.flags); +} + +void test_subscription_t(void) { + uvwasi_subscription_t subscription = { + .userdata = 0xabcdabcdabcdabcdllu, + .type = UVWASI_EVENTTYPE_CLOCK, + .u = { + .clock = { + .clock_id = 0xabcdabcdu, + .timeout = 0xabcdabcdabcdabcdllu, + .precision = 0xdcbadcbadcbadcballu, + .flags = UVWASI_SUBSCRIPTION_CLOCK_ABSTIME + } + } + }; + + char data[ADD_CANARIES(subscription_t)] = { 0 }; + use_canaries(data, sizeof(data)); + uvwasi_serdes_write_subscription_t(data, sizeof(canary), &subscription); + check_canaries(data, sizeof(data)); + /* TODO(tniessen): Check result of serialization. */ + + uvwasi_subscription_t deserialized = { 0 }; + uvwasi_serdes_read_subscription_t(data, sizeof(canary), &deserialized); + assert(deserialized.userdata == subscription.userdata); + assert(deserialized.type == subscription.type); + assert(deserialized.u.clock.clock_id == subscription.u.clock.clock_id); + assert(deserialized.u.clock.timeout == subscription.u.clock.timeout); + assert(deserialized.u.clock.precision == subscription.u.clock.precision); + assert(deserialized.u.clock.flags == subscription.u.clock.flags); + + subscription.type = UVWASI_EVENTTYPE_FD_READ; + subscription.u.fd_readwrite.fd = 0xabcdabcdu; + uvwasi_serdes_write_subscription_t(data, sizeof(canary), &subscription); + /* TODO(tniessen): Check result of serialization. */ + + memset(&deserialized, 0, sizeof(deserialized)); + uvwasi_serdes_read_subscription_t(data, sizeof(canary), &deserialized); + assert(deserialized.userdata == subscription.userdata); + assert(deserialized.type == subscription.type); + assert(deserialized.u.fd_readwrite.fd == subscription.u.fd_readwrite.fd); + + subscription.type = UVWASI_EVENTTYPE_FD_WRITE; + uvwasi_serdes_write_subscription_t(data, sizeof(canary), &subscription); + /* TODO(tniessen): Check result of serialization. */ + + memset(&deserialized, 0, sizeof(deserialized)); + uvwasi_serdes_read_subscription_t(data, sizeof(canary), &deserialized); + assert(deserialized.userdata == subscription.userdata); + assert(deserialized.type == subscription.type); + assert(deserialized.u.fd_readwrite.fd == subscription.u.fd_readwrite.fd); +}