You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
17 KiB
428 lines
17 KiB
#ifndef __WASI_SERDES_H__
|
|
#define __WASI_SERDES_H__
|
|
|
|
/*
|
|
The serialization/deserialization logic in this file is derived from uvwasi source code
|
|
located at the following URL:
|
|
https://github.com/nodejs/uvwasi/blob/7523499546c351a1642442e6ec7bd32bcac76e2c/src/wasi_serdes.c
|
|
|
|
It retains the the Node.js license as follows:
|
|
|
|
MIT License
|
|
-----------
|
|
|
|
Copyright (c) 2019 Colin Ihrig and Contributors
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#include "wasi.h"
|
|
|
|
/* Basic uint{8,16,32,64}_t read/write functions. */
|
|
|
|
#define BASIC_TYPE(name, type) \
|
|
static inline void wasi_serdes_write_##name(void *ptr, size_t offset, type value); \
|
|
static inline type wasi_serdes_read_##name(const void *ptr, size_t offset);
|
|
|
|
#define BASIC_TYPE_WASI(type) BASIC_TYPE(type, __wasi_##type)
|
|
|
|
#define WASI_SERDES_SIZE_uint8_t sizeof(uint8_t)
|
|
BASIC_TYPE(uint8_t, uint8_t)
|
|
#define WASI_SERDES_SIZE_uint16_t sizeof(uint16_t)
|
|
BASIC_TYPE(uint16_t, uint16_t)
|
|
#define WASI_SERDES_SIZE_uint32_t sizeof(uint32_t)
|
|
BASIC_TYPE(uint32_t, uint32_t)
|
|
#define WASI_SERDES_SIZE_uint64_t sizeof(uint64_t)
|
|
BASIC_TYPE(uint64_t, uint64_t)
|
|
|
|
#define WASI_SERDES_SIZE_advice_t sizeof(__wasi_advice_t)
|
|
BASIC_TYPE_WASI(advice_t)
|
|
#define WASI_SERDES_SIZE_clockid_t sizeof(__wasi_clockid_t)
|
|
BASIC_TYPE_WASI(clockid_t)
|
|
#define WASI_SERDES_SIZE_device_t sizeof(__wasi_device_t)
|
|
BASIC_TYPE_WASI(device_t)
|
|
#define WASI_SERDES_SIZE_dircookie_t sizeof(__wasi_dircookie_t)
|
|
BASIC_TYPE_WASI(dircookie_t)
|
|
#define WASI_SERDES_SIZE_eventrwflags_t sizeof(__wasi_eventrwflags_t)
|
|
BASIC_TYPE_WASI(eventrwflags_t)
|
|
#define WASI_SERDES_SIZE_eventtype_t sizeof(__wasi_eventtype_t)
|
|
BASIC_TYPE_WASI(eventtype_t)
|
|
#define WASI_SERDES_SIZE_exitcode_t sizeof(__wasi_exitcode_t)
|
|
BASIC_TYPE_WASI(exitcode_t)
|
|
#define WASI_SERDES_SIZE_fd_t sizeof(__wasi_fd_t)
|
|
BASIC_TYPE_WASI(fd_t)
|
|
#define WASI_SERDES_SIZE_fdflags_t sizeof(__wasi_fdflags_t)
|
|
BASIC_TYPE_WASI(fdflags_t)
|
|
#define WASI_SERDES_SIZE_filesize_t sizeof(__wasi_filesize_t)
|
|
BASIC_TYPE_WASI(filesize_t)
|
|
#define WASI_SERDES_SIZE_fstflags_t sizeof(__wasi_fstflags_t)
|
|
BASIC_TYPE_WASI(fstflags_t)
|
|
#define WASI_SERDES_SIZE_inode_t sizeof(__wasi_inode_t)
|
|
BASIC_TYPE_WASI(inode_t)
|
|
#define WASI_SERDES_SIZE_linkcount_t sizeof(__wasi_linkcount_t)
|
|
BASIC_TYPE_WASI(linkcount_t)
|
|
#define WASI_SERDES_SIZE_lookupflags_t sizeof(__wasi_lookupflags_t)
|
|
BASIC_TYPE_WASI(lookupflags_t)
|
|
#define WASI_SERDES_SIZE_oflags_t sizeof(__wasi_oflags_t)
|
|
BASIC_TYPE_WASI(oflags_t)
|
|
#define WASI_SERDES_SIZE_preopentype_t sizeof(__wasi_preopentype_t)
|
|
BASIC_TYPE_WASI(preopentype_t)
|
|
#define WASI_SERDES_SIZE_riflags_t sizeof(__wasi_riflags_t)
|
|
BASIC_TYPE_WASI(riflags_t)
|
|
#define WASI_SERDES_SIZE_rights_t sizeof(__wasi_rights_t)
|
|
BASIC_TYPE_WASI(rights_t)
|
|
#define WASI_SERDES_SIZE_roflags_t sizeof(__wasi_roflags_t)
|
|
BASIC_TYPE_WASI(roflags_t)
|
|
#define WASI_SERDES_SIZE_sdflags_t sizeof(__wasi_sdflags_t)
|
|
BASIC_TYPE_WASI(sdflags_t)
|
|
#define WASI_SERDES_SIZE_siflags_t sizeof(__wasi_siflags_t)
|
|
BASIC_TYPE_WASI(siflags_t)
|
|
#define WASI_SERDES_SIZE_size_t sizeof(__wasi_size_t)
|
|
BASIC_TYPE_WASI(size_t)
|
|
#define WASI_SERDES_SIZE_inode_t sizeof(__wasi_inode_t)
|
|
BASIC_TYPE_WASI(inode_t)
|
|
#define WASI_SERDES_SIZE_signal_t sizeof(__wasi_signal_t)
|
|
BASIC_TYPE_WASI(signal_t)
|
|
#define WASI_SERDES_SIZE_subclockflags_t sizeof(__wasi_subclockflags_t)
|
|
BASIC_TYPE_WASI(subclockflags_t)
|
|
#define WASI_SERDES_SIZE_timestamp_t sizeof(__wasi_timestamp_t)
|
|
BASIC_TYPE_WASI(timestamp_t)
|
|
#define WASI_SERDES_SIZE_userdata_t sizeof(__wasi_userdata_t)
|
|
BASIC_TYPE_WASI(userdata_t)
|
|
#define WASI_SERDES_SIZE_whence_t sizeof(__wasi_whence_t)
|
|
BASIC_TYPE_WASI(whence_t)
|
|
|
|
#undef BASIC_TYPE_WASI
|
|
#undef BASIC_TYPE
|
|
|
|
/* WASI structure read/write functions. */
|
|
|
|
#define STRUCT(name) \
|
|
void wasi_serdes_write_##name(void *ptr, size_t offset, const __wasi_##name *value); \
|
|
void wasi_serdes_read_##name(const void *ptr, size_t offset, __wasi_##name *value);
|
|
|
|
/* iovs currently only need to be read from WASM memory. */
|
|
#define IOVS_STRUCT(name) \
|
|
static inline __wasi_errno_t wasi_serdes_read_##name(const void *ptr, size_t end, size_t offset, \
|
|
__wasi_##name *value);
|
|
|
|
#define WASI_SERDES_SIZE_ciovec_t 8
|
|
IOVS_STRUCT(ciovec_t)
|
|
|
|
#define WASI_SERDES_SIZE_iovec_t 8
|
|
IOVS_STRUCT(iovec_t)
|
|
|
|
#define WASI_SERDES_SIZE_dirent_t 24
|
|
STRUCT(dirent_t)
|
|
|
|
#define WASI_SERDES_SIZE_fdstat_t 24
|
|
STRUCT(fdstat_t)
|
|
|
|
#define WASI_SERDES_SIZE_filestat_t 64
|
|
STRUCT(filestat_t)
|
|
|
|
#define WASI_SERDES_SIZE_prestat_t 8
|
|
STRUCT(prestat_t)
|
|
|
|
#define WASI_SERDES_SIZE_event_t 32
|
|
STRUCT(event_t)
|
|
|
|
#define WASI_SERDES_SIZE_subscription_t 48
|
|
STRUCT(subscription_t)
|
|
|
|
#undef STRUCT
|
|
#undef IOVS_STRUCT
|
|
|
|
static inline __wasi_errno_t
|
|
wasi_serdes_readv_ciovec_t(const void *ptr, size_t end, size_t offset, __wasi_ciovec_t *iovs, __wasi_size_t iovs_len);
|
|
|
|
static inline __wasi_errno_t
|
|
wasi_serdes_readv_iovec_t(const void *ptr, size_t end, size_t offset, __wasi_iovec_t *iovs, __wasi_size_t iovs_len);
|
|
|
|
/* Helper functions for memory bounds checking. */
|
|
static inline int wasi_serdes_check_bounds(size_t offset, size_t end, size_t size);
|
|
static inline int wasi_serdes_check_array_bounds(size_t offset, size_t end, size_t size, size_t count);
|
|
|
|
static inline int
|
|
wasi_serdes_check_bounds(size_t offset, size_t end, size_t size)
|
|
{
|
|
return end > offset && size <= (end - offset);
|
|
}
|
|
|
|
static inline void
|
|
wasi_serdes_write_uint64_t(void *ptr, size_t offset, uint64_t value)
|
|
{
|
|
wasi_serdes_write_uint32_t(ptr, offset, (uint32_t)value);
|
|
wasi_serdes_write_uint32_t(ptr, offset + 4, value >> 32);
|
|
}
|
|
|
|
static inline void
|
|
wasi_serdes_write_uint32_t(void *ptr, size_t offset, uint32_t value)
|
|
{
|
|
wasi_serdes_write_uint16_t(ptr, offset, (uint16_t)value);
|
|
wasi_serdes_write_uint16_t(ptr, offset + 2, value >> 16);
|
|
}
|
|
|
|
static inline void
|
|
wasi_serdes_write_uint16_t(void *ptr, size_t offset, uint16_t value)
|
|
{
|
|
wasi_serdes_write_uint8_t(ptr, offset, (uint8_t)value);
|
|
wasi_serdes_write_uint8_t(ptr, offset + 1, value >> 8);
|
|
}
|
|
|
|
static inline void
|
|
wasi_serdes_write_uint8_t(void *ptr, size_t offset, uint8_t value)
|
|
{
|
|
((uint8_t *)ptr)[offset] = value;
|
|
}
|
|
|
|
static inline uint64_t
|
|
wasi_serdes_read_uint64_t(const void *ptr, size_t offset)
|
|
{
|
|
uint64_t low = wasi_serdes_read_uint32_t(ptr, offset);
|
|
uint64_t high = wasi_serdes_read_uint32_t(ptr, offset + 4);
|
|
return low | (high << 32);
|
|
}
|
|
|
|
static inline uint32_t
|
|
wasi_serdes_read_uint32_t(const void *ptr, size_t offset)
|
|
{
|
|
uint32_t low = wasi_serdes_read_uint16_t(ptr, offset);
|
|
uint32_t high = wasi_serdes_read_uint16_t(ptr, offset + 2);
|
|
return low | (high << 16);
|
|
}
|
|
|
|
static inline uint16_t
|
|
wasi_serdes_read_uint16_t(const void *ptr, size_t offset)
|
|
{
|
|
uint16_t low = wasi_serdes_read_uint8_t(ptr, offset);
|
|
uint16_t high = wasi_serdes_read_uint8_t(ptr, offset + 1);
|
|
return low | (high << 8);
|
|
}
|
|
|
|
static inline uint8_t
|
|
wasi_serdes_read_uint8_t(const void *ptr, size_t offset)
|
|
{
|
|
return ((const uint8_t *)ptr)[offset];
|
|
}
|
|
|
|
#define TYPE_SWITCH switch (value->type)
|
|
#define TAG_SWITCH switch (value->u.tag)
|
|
|
|
#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(size_t, uint32_t) \
|
|
ALIAS(subclockflags_t, uint16_t) \
|
|
ALIAS(timestamp_t, uint64_t) \
|
|
ALIAS(userdata_t, uint64_t) \
|
|
ALIAS(whence_t, uint8_t) \
|
|
\
|
|
STRUCT(dirent_t) \
|
|
{ \
|
|
FIELD(0, dircookie_t, d_next); \
|
|
FIELD(8, inode_t, d_ino); \
|
|
FIELD(16, uint32_t, d_namlen); \
|
|
FIELD(20, filetype_t, d_type); \
|
|
} \
|
|
\
|
|
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, dev); \
|
|
FIELD(8, inode_t, ino); \
|
|
FIELD(16, filetype_t, filetype); \
|
|
FIELD(24, linkcount_t, nlink); \
|
|
FIELD(32, filesize_t, size); \
|
|
FIELD(40, timestamp_t, atim); \
|
|
FIELD(48, timestamp_t, mtim); \
|
|
FIELD(56, timestamp_t, ctim); \
|
|
} \
|
|
\
|
|
STRUCT(prestat_t) \
|
|
{ \
|
|
FIELD(0, preopentype_t, tag); \
|
|
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 __WASI_EVENTTYPE_FD_READ: \
|
|
case __WASI_EVENTTYPE_FD_WRITE: \
|
|
FIELD(16, filesize_t, fd_readwrite.nbytes); \
|
|
FIELD(24, eventrwflags_t, fd_readwrite.flags); \
|
|
} \
|
|
} \
|
|
\
|
|
STRUCT(subscription_t) \
|
|
{ \
|
|
FIELD(0, userdata_t, userdata); \
|
|
FIELD(8, eventtype_t, u.tag); \
|
|
TAG_SWITCH \
|
|
{ \
|
|
case __WASI_EVENTTYPE_CLOCK: \
|
|
FIELD(16, clockid_t, u.u.clock.id); \
|
|
FIELD(24, timestamp_t, u.u.clock.timeout); \
|
|
FIELD(32, timestamp_t, u.u.clock.precision); \
|
|
FIELD(40, subclockflags_t, u.u.clock.flags); \
|
|
break; \
|
|
case __WASI_EVENTTYPE_FD_READ: \
|
|
FIELD(16, fd_t, u.u.fd_read.file_descriptor); \
|
|
break; \
|
|
case __WASI_EVENTTYPE_FD_WRITE: \
|
|
FIELD(16, fd_t, u.u.fd_write.file_descriptor); \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
#define WRITE_STRUCT(name) void wasi_serdes_write_##name(void *ptr, size_t offset, const __wasi_##name *value)
|
|
|
|
#define READ_STRUCT(name) void wasi_serdes_read_##name(const void *ptr, size_t offset, __wasi_##name *value)
|
|
|
|
#define WRITE_FIELD(field_offset, type, field) \
|
|
do { \
|
|
wasi_serdes_write_##type(ptr, offset + field_offset, value->field); \
|
|
} while (0)
|
|
|
|
#define READ_FIELD(field_offset, type, field) \
|
|
do { \
|
|
value->field = wasi_serdes_read_##type(ptr, offset + field_offset); \
|
|
} while (0)
|
|
|
|
#define WRITE_ALIAS(new_name, old_name) \
|
|
void wasi_serdes_write_##new_name(void *ptr, size_t offset, __wasi_##new_name value) \
|
|
{ \
|
|
wasi_serdes_write_##old_name(ptr, offset, value); \
|
|
}
|
|
|
|
#define READ_ALIAS(new_name, old_name) \
|
|
__wasi_##new_name wasi_serdes_read_##new_name(const void *ptr, size_t offset) \
|
|
{ \
|
|
return wasi_serdes_read_##old_name(ptr, offset); \
|
|
}
|
|
|
|
ALL_TYPES(WRITE_STRUCT, WRITE_FIELD, WRITE_ALIAS)
|
|
ALL_TYPES(READ_STRUCT, READ_FIELD, READ_ALIAS)
|
|
|
|
|
|
static inline __wasi_errno_t
|
|
wasi_serdes_read_ciovec_t(const void *ptr, size_t end, size_t offset, __wasi_ciovec_t *value)
|
|
{
|
|
uint32_t buf_ptr;
|
|
|
|
buf_ptr = wasi_serdes_read_uint32_t(ptr, offset);
|
|
value->buf_len = wasi_serdes_read_size_t(ptr, offset + 4);
|
|
|
|
if (!wasi_serdes_check_bounds(buf_ptr, end, value->buf_len)) return __WASI_ERRNO_OVERFLOW;
|
|
|
|
value->buf = ((uint8_t *)ptr + buf_ptr);
|
|
return __WASI_ERRNO_SUCCESS;
|
|
}
|
|
|
|
|
|
static inline __wasi_errno_t
|
|
wasi_serdes_read_iovec_t(const void *ptr, size_t end, size_t offset, __wasi_iovec_t *value)
|
|
{
|
|
uint32_t buf_ptr;
|
|
|
|
buf_ptr = wasi_serdes_read_uint32_t(ptr, offset);
|
|
value->buf_len = wasi_serdes_read_size_t(ptr, offset + 4);
|
|
|
|
if (!wasi_serdes_check_bounds(buf_ptr, end, value->buf_len)) return __WASI_ERRNO_OVERFLOW;
|
|
|
|
value->buf = ((uint8_t *)ptr + buf_ptr);
|
|
return __WASI_ERRNO_SUCCESS;
|
|
}
|
|
|
|
|
|
static inline __wasi_errno_t
|
|
wasi_serdes_readv_ciovec_t(const void *ptr, size_t end, size_t offset, __wasi_ciovec_t *iovs, __wasi_size_t iovs_len)
|
|
{
|
|
__wasi_errno_t err;
|
|
__wasi_size_t i;
|
|
|
|
for (i = 0; i < iovs_len; i++) {
|
|
err = wasi_serdes_read_ciovec_t(ptr, end, offset, &iovs[i]);
|
|
if (err != __WASI_ERRNO_SUCCESS) return err;
|
|
offset += WASI_SERDES_SIZE_ciovec_t;
|
|
}
|
|
|
|
return __WASI_ERRNO_SUCCESS;
|
|
}
|
|
|
|
|
|
static inline __wasi_errno_t
|
|
wasi_serdes_readv_iovec_t(const void *ptr, size_t end, size_t offset, __wasi_iovec_t *iovs, __wasi_size_t iovs_len)
|
|
{
|
|
__wasi_errno_t err;
|
|
__wasi_size_t i;
|
|
|
|
for (i = 0; i < iovs_len; i++) {
|
|
err = wasi_serdes_read_iovec_t(ptr, end, offset, &iovs[i]);
|
|
if (err != __WASI_ERRNO_SUCCESS) return err;
|
|
offset += WASI_SERDES_SIZE_iovec_t;
|
|
}
|
|
|
|
return __WASI_ERRNO_SUCCESS;
|
|
}
|
|
|
|
static inline int
|
|
wasi_serdes_check_array_bounds(size_t offset, size_t end, size_t size, size_t count)
|
|
{
|
|
return end > offset && ((count * size) / size == count) && (count * size <= end - offset);
|
|
}
|
|
|
|
|
|
#endif /* __WASI_SERDES_H__ */
|