Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
131 changes: 131 additions & 0 deletions include/serdes.h
Original file line number Diff line number Diff line change
@@ -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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UVWASI_SERDES_SIZE_prestat_t doesn't look correct to me. I see that you're serializing u.dir.pr_name_len as a uint32_t but its type is size_t (i.e., 64 bits on 64 bits architectures.)

I suppose that realistically it always fits in a uint32_t but I'd still add some kind of range check.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused about this part myself. WASI requires it to be a size_t, but I am not sure if that's a host type size_t or a WASM type size_t. Node.js currently uses 32 bits.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with 32 bits because that's what the wasi-libc did here.

Also, according to this ABI doc, size_t is defined as unsigned long, which has a sizeof four, and is represented as a wasm i32.


#define UVWASI_SERDES_SIZE_event_t 32
STRUCT(event_t)
Comment thread
bnoordhuis marked this conversation as resolved.

#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__ */
1 change: 1 addition & 0 deletions include/uvwasi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
170 changes: 170 additions & 0 deletions src/serdes.c
Original file line number Diff line number Diff line change
@@ -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)
Loading