diff --git a/include/wasi_serdes.h b/include/wasi_serdes.h index cb05e3d..f927b82 100644 --- a/include/wasi_serdes.h +++ b/include/wasi_serdes.h @@ -84,13 +84,26 @@ BASIC_TYPE_UVWASI(whence_t) /* WASI structure read/write functions. */ -#define STRUCT(name) \ +#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); \ + uvwasi_##name* value); + +/* iovs currently only need to be read from WASM memory. */ +#define IOVS_STRUCT(name) \ + uvwasi_errno_t uvwasi_serdes_read_##name(const void* ptr, \ + size_t end, \ + size_t offset, \ + uvwasi_##name* value); + +#define UVWASI_SERDES_SIZE_ciovec_t 8 +IOVS_STRUCT(ciovec_t) + +#define UVWASI_SERDES_SIZE_iovec_t 8 +IOVS_STRUCT(iovec_t) #define UVWASI_SERDES_SIZE_fdstat_t 24 STRUCT(fdstat_t) @@ -108,20 +121,25 @@ STRUCT(event_t) 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))) \ +#undef IOVS_STRUCT + +uvwasi_errno_t uvwasi_serdes_readv_ciovec_t(const void* ptr, + size_t end, + size_t offset, + uvwasi_ciovec_t* iovs, + uvwasi_size_t iovs_len); + +uvwasi_errno_t uvwasi_serdes_readv_iovec_t(const void* ptr, + size_t end, + size_t offset, + uvwasi_iovec_t* iovs, + uvwasi_size_t iovs_len); + +/* Helper functions for memory bounds checking. */ +int uvwasi_serdes_check_bounds(size_t offset, size_t end, size_t size); +int uvwasi_serdes_check_array_bounds(size_t offset, + size_t end, + size_t size, + size_t count); #endif /* __UVWASI_SERDES_H__ */ diff --git a/src/wasi_serdes.c b/src/wasi_serdes.c index 3fcb4b4..96253fc 100644 --- a/src/wasi_serdes.c +++ b/src/wasi_serdes.c @@ -170,3 +170,90 @@ uint8_t uvwasi_serdes_read_uint8_t(const void* ptr, size_t offset) { ALL_TYPES(WRITE_STRUCT, WRITE_FIELD, WRITE_ALIAS) ALL_TYPES(READ_STRUCT, READ_FIELD, READ_ALIAS) + + +uvwasi_errno_t uvwasi_serdes_read_ciovec_t(const void* ptr, + size_t end, + size_t offset, + uvwasi_ciovec_t* value) { + uint32_t buf_ptr; + + buf_ptr = uvwasi_serdes_read_uint32_t(ptr, offset); + value->buf_len = uvwasi_serdes_read_size_t(ptr, offset + 4); + + if (!uvwasi_serdes_check_bounds(buf_ptr, end, value->buf_len)) + return UVWASI_EOVERFLOW; + + value->buf = ((uint8_t*) ptr + buf_ptr); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_serdes_read_iovec_t(const void* ptr, + size_t end, + size_t offset, + uvwasi_iovec_t* value) { + uint32_t buf_ptr; + + buf_ptr = uvwasi_serdes_read_uint32_t(ptr, offset); + value->buf_len = uvwasi_serdes_read_size_t(ptr, offset + 4); + + if (!uvwasi_serdes_check_bounds(buf_ptr, end, value->buf_len)) + return UVWASI_EOVERFLOW; + + value->buf = ((uint8_t*) ptr + buf_ptr); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_serdes_readv_ciovec_t(const void* ptr, + size_t end, + size_t offset, + uvwasi_ciovec_t* iovs, + uvwasi_size_t iovs_len) { + uvwasi_errno_t err; + uvwasi_size_t i; + + for (i = 0; i < iovs_len; i++) { + err = uvwasi_serdes_read_ciovec_t(ptr, end, offset, &iovs[i]); + if (err != UVWASI_ESUCCESS) + return err; + offset += UVWASI_SERDES_SIZE_ciovec_t; + } + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_serdes_readv_iovec_t(const void* ptr, + size_t end, + size_t offset, + uvwasi_iovec_t* iovs, + uvwasi_size_t iovs_len) { + uvwasi_errno_t err; + uvwasi_size_t i; + + for (i = 0; i < iovs_len; i++) { + err = uvwasi_serdes_read_iovec_t(ptr, end, offset, &iovs[i]); + if (err != UVWASI_ESUCCESS) + return err; + offset += UVWASI_SERDES_SIZE_iovec_t; + } + + return UVWASI_ESUCCESS; +} + + +int uvwasi_serdes_check_bounds(size_t offset, size_t end, size_t size) { + return end > offset && size <= (end - offset); +} + + +int uvwasi_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); +} diff --git a/test/test-serdes.c b/test/test-serdes.c index 18b762f..b36e4dc 100644 --- a/test/test-serdes.c +++ b/test/test-serdes.c @@ -44,43 +44,47 @@ void check_canaries(const char* ptr, size_t size) { } 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, + assert(!uvwasi_serdes_check_array_bounds(0, + 0xffffffffffffffffllu, + UVWASI_SERDES_SIZE_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)); + assert(uvwasi_serdes_check_bounds(19, 20, UVWASI_SERDES_SIZE_uint8_t)); + assert(!uvwasi_serdes_check_bounds(20, 20, UVWASI_SERDES_SIZE_uint8_t)); + assert(uvwasi_serdes_check_bounds(18, 20, UVWASI_SERDES_SIZE_uint16_t)); + assert(!uvwasi_serdes_check_bounds(19, 20, UVWASI_SERDES_SIZE_uint16_t)); + assert(uvwasi_serdes_check_bounds(16, 20, UVWASI_SERDES_SIZE_uint32_t)); + assert(!uvwasi_serdes_check_bounds(17, 20, UVWASI_SERDES_SIZE_uint32_t)); + assert(uvwasi_serdes_check_bounds(12, 20, UVWASI_SERDES_SIZE_uint64_t)); + assert(!uvwasi_serdes_check_bounds(13, 20, UVWASI_SERDES_SIZE_uint64_t)); + assert(uvwasi_serdes_check_bounds(0, 24, UVWASI_SERDES_SIZE_fdstat_t)); + assert(!uvwasi_serdes_check_bounds(1, 24, UVWASI_SERDES_SIZE_fdstat_t)); + assert(uvwasi_serdes_check_bounds(0, 64, UVWASI_SERDES_SIZE_filestat_t)); + assert(!uvwasi_serdes_check_bounds(1, 64, UVWASI_SERDES_SIZE_filestat_t)); + assert(uvwasi_serdes_check_bounds(0, 8, UVWASI_SERDES_SIZE_prestat_t)); + assert(!uvwasi_serdes_check_bounds(1, 8, UVWASI_SERDES_SIZE_prestat_t)); + assert(uvwasi_serdes_check_bounds(0, 32, UVWASI_SERDES_SIZE_event_t)); + assert(!uvwasi_serdes_check_bounds(1, 32, UVWASI_SERDES_SIZE_event_t)); + assert(uvwasi_serdes_check_bounds(0, 48, UVWASI_SERDES_SIZE_subscription_t)); + assert(!uvwasi_serdes_check_bounds(1, 48, UVWASI_SERDES_SIZE_subscription_t)); + + assert(uvwasi_serdes_check_array_bounds(0, + 480, + UVWASI_SERDES_SIZE_subscription_t, + 10)); + assert(!uvwasi_serdes_check_array_bounds(1, + 480, + UVWASI_SERDES_SIZE_subscription_t, + 10)); + assert(uvwasi_serdes_check_array_bounds(0, + 8000, + UVWASI_SERDES_SIZE_inode_t, + 1000)); + assert(!uvwasi_serdes_check_array_bounds(1, + 8000, + UVWASI_SERDES_SIZE_inode_t, + 1000)); } void test_basic_types(void) {