diff --git a/src/wasi/WASI.cpp b/src/wasi/WASI.cpp index f6e928c50..4371b1e0e 100644 --- a/src/wasi/WASI.cpp +++ b/src/wasi/WASI.cpp @@ -41,14 +41,23 @@ static void* get_memory_pointer(Instance* instance, Value& value, size_t size) return memory->buffer() + offset; } +template class TemporaryData { public: TemporaryData(size_t size) { + /* Check that more memory is requested than the maximum provided by malloc. */ + if (size > (std::numeric_limits::max() / sizeof(T))) { + m_data = nullptr; + return; + } + + size *= sizeof(T); + if (size <= sizeof(m_stackData)) { m_data = m_stackData; } else { - m_data = malloc(size); + m_data = reinterpret_cast(malloc(size)); } } @@ -59,14 +68,14 @@ class TemporaryData { } } - void* data() + T* data() { return m_data; } private: - void* m_stackData[8]; - void* m_data; + T m_stackData[maxSize]; + T* m_data; }; void WASI::initialize(uvwasi_t* uvwasi) @@ -123,51 +132,78 @@ void WASI::clock_time_get(ExecutionState& state, Value* argv, Value* result, Ins void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance) { uint32_t fd = argv[0].asI32(); - uint32_t iovptr = argv[1].asI32(); - uint32_t iovcnt = argv[2].asI32(); - uint32_t out = argv[3].asI32(); + size_t iovsLen = static_cast(argv[2].asI32()); + uint32_t* nwritten = reinterpret_cast(get_memory_pointer(instance, argv[3], sizeof(uint32_t))); + uint32_t* iovptr = reinterpret_cast(get_memory_pointer(instance, argv[1], iovsLen * (sizeof(uint32_t) << 1))); - if (uint64_t(iovptr) + iovcnt >= instance->memory(0)->sizeInByte()) { - result[0] = Value(static_cast(WasiErrNo::inval)); + if (iovptr == nullptr || nwritten == nullptr) { + result[0] = Value(WasiErrNo::inval); return; } - std::vector iovs(iovcnt); - for (uint32_t i = 0; i < iovcnt; i++) { - iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast(instance->memory(0)->buffer() + iovptr + i * 8); - iovs[i].buf_len = *reinterpret_cast(instance->memory(0)->buffer() + iovptr + 4 + i * 8); - } + TemporaryData iovsBuffer(iovsLen); + uvwasi_ciovec_t* iovs = iovsBuffer.data(); + uint64_t sizeInByte = instance->memory(0)->sizeInByte(); + uint8_t* buffer = instance->memory(0)->buffer(); - uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out); + for (uint32_t i = 0; i < iovsLen; i++) { + if (iovptr[1] > sizeInByte || iovptr[0] > sizeInByte - iovptr[1]) { + result[0] = Value(WasiErrNo::inval); + return; + } + + iovs[i].buf = buffer + iovptr[0]; + iovs[i].buf_len = iovptr[1]; + iovptr += 2; + } - result[0] = Value(static_cast(uvwasi_fd_write(WASI::g_uvwasi, fd, iovs.data(), iovs.size(), out_addr))); - *(instance->memory(0)->buffer() + out) = *out_addr; + result[0] = Value(uvwasi_fd_write(WASI::g_uvwasi, fd, iovs, iovsLen, nwritten)); } void WASI::fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance) { uint32_t fd = argv[0].asI32(); - uint32_t iovptr = argv[1].asI32(); - uint32_t iovcnt = argv[2].asI32(); - uint32_t out = argv[3].asI32(); - - std::vector iovs(iovcnt); - for (uint32_t i = 0; i < iovcnt; i++) { - iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast(instance->memory(0)->buffer() + iovptr + i * 8); - iovs[i].buf_len = *reinterpret_cast(instance->memory(0)->buffer() + iovptr + 4 + i * 8); + size_t iovsLen = static_cast(argv[2].asI32()); + uint32_t* nread = reinterpret_cast(get_memory_pointer(instance, argv[3], sizeof(uint32_t))); + uint32_t* iovptr = reinterpret_cast(get_memory_pointer(instance, argv[1], iovsLen * (sizeof(uint32_t) << 1))); + + if (iovptr == nullptr || nread == nullptr) { + result[0] = Value(WasiErrNo::inval); + return; } - uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out); + TemporaryData iovsBuffer(iovsLen); + uvwasi_iovec_t* iovs = iovsBuffer.data(); + uint64_t sizeInByte = instance->memory(0)->sizeInByte(); + uint8_t* buffer = instance->memory(0)->buffer(); + + for (uint32_t i = 0; i < iovsLen; i++) { + if (iovptr[1] > sizeInByte || iovptr[0] > sizeInByte - iovptr[1]) { + result[0] = Value(WasiErrNo::inval); + return; + } + + iovs[i].buf = buffer + iovptr[0]; + iovs[i].buf_len = iovptr[1]; + iovptr += 2; + } - result[0] = Value(static_cast(uvwasi_fd_read(WASI::g_uvwasi, fd, iovs.data(), iovs.size(), out_addr))); - *(instance->memory(0)->buffer() + out) = *out_addr; + result[0] = Value(uvwasi_fd_read(WASI::g_uvwasi, fd, iovs, iovsLen, nread)); } void WASI::fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance) { uint32_t fd = argv[0].asI32(); - result[0] = Value(static_cast(uvwasi_fd_close(WASI::g_uvwasi, fd))); + result[0] = Value(uvwasi_fd_close(WASI::g_uvwasi, fd)); +} + +void WASI::fd_fdstat_get(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uint32_t fd = argv[0].asI32(); + uvwasi_fdstat_t* fdstat = reinterpret_cast(get_memory_pointer(instance, argv[1], sizeof(uvwasi_fdstat_t))); + + result[0] = Value(uvwasi_fd_fdstat_get(WASI::g_uvwasi, fd, fdstat)); } void WASI::fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance) @@ -218,7 +254,7 @@ void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instan return; } - TemporaryData pointers(count * sizeof(void*)); + TemporaryData pointers(count); if (pointers.data() == nullptr) { result[0] = Value(WasiErrNo::inval); diff --git a/src/wasi/WASI.h b/src/wasi/WASI.h index 7c3dd8d7e..924b215f3 100644 --- a/src/wasi/WASI.h +++ b/src/wasi/WASI.h @@ -44,6 +44,7 @@ class WASI { F(fd_write, I32I32I32I32_RI32) \ F(fd_read, I32I32I32I32_RI32) \ F(fd_close, I32_RI32) \ + F(fd_fdstat_get, I32I32_RI32) \ F(fd_seek, I32I64I32I32_RI32) \ F(path_open, I32I32I32I32I32I64I64I32I32_RI32) \ F(environ_get, I32I32_RI32) \ @@ -159,6 +160,7 @@ class WASI { static void fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance); + static void fd_fdstat_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); diff --git a/test/wasi/fd_fdstat_get.wast b/test/wasi/fd_fdstat_get.wast new file mode 100644 index 000000000..1f1868849 --- /dev/null +++ b/test/wasi/fd_fdstat_get.wast @@ -0,0 +1,60 @@ +(module + (import "wasi_snapshot_preview1" "path_open" (func $path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_close" (func $fd_close (param i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_fdstat_get" (func $fd_fdstat_get (param i32 i32) (result i32))) + + (memory 1 1) + (data (i32.const 100) "./fd_fdstat_get.wast") + + (func (export "fdstatGet") (result i32 i32 i32) + i32.const 3 ;; Directory file descriptior, by default 3 is the first opened directory + i32.const 1 ;; uvwasi_lookupflags_t: UVWASI_LOOKUP_SYMLINK_FOLLOW + i32.const 100 ;; Offset of file name in memory + i32.const 20 ;; Length of file name + i32.const 0 ;; uvwasi_oflags_t: none + i64.const 0x42000 ;; base uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i64.const 0x42000 ;; inherited uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i32.const 0 ;; uvwasi_fdflags_t: none + i32.const 0 ;; Offset to store at the opened file descriptor in memory + call $path_open + + i32.const 0 + i32.load + i32.const 200 + call $fd_fdstat_get + + i32.const 0 + i32.load + call $fd_close + ) + + (func (export "fdstatGetBad") (result i32 i32 i32 i32) + i32.const 3 ;; Directory file descriptior, by default 3 is the first opened directory + i32.const 1 ;; uvwasi_lookupflags_t: UVWASI_LOOKUP_SYMLINK_FOLLOW + i32.const 100 ;; Offset of file name in memory + i32.const 20 ;; Length of file name + i32.const 0 ;; uvwasi_oflags_t: none + i64.const 0x42000 ;; base uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i64.const 0x42000 ;; inherited uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i32.const 0 ;; uvwasi_fdflags_t: none + i32.const 0 ;; Offset to store at the opened file descriptor in memory + call $path_open + + i32.const 0 + i32.load + i32.const 65535 + call $fd_fdstat_get + + i32.const -1 + i32.const 200 + call $fd_fdstat_get + + i32.const 0 + i32.load + call $fd_close + ) +) + +(assert_return (invoke "fdstatGet") (i32.const 0) (i32.const 0) (i32.const 0)) +(assert_return (invoke "fdstatGetBad") (i32.const 0) (i32.const 28) (i32.const 8) (i32.const 0)) + diff --git a/test/wasi/print.wast b/test/wasi/print.wast new file mode 100644 index 000000000..3917ccabb --- /dev/null +++ b/test/wasi/print.wast @@ -0,0 +1,69 @@ +(module + (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) + + (memory $memory 1 1) + (data (i32.const 100) "Hello ") + (data (i32.const 200) "World!\n") + + (func (export "writeToStdout") (result i32 i32) + ;; Write iovs into memory from offset 16 + i32.const 16 + i32.const 100 + i32.store + + i32.const 20 + i32.const 6 + i32.store + + i32.const 24 + i32.const 200 + i32.store + + i32.const 28 + i32.const 7 + i32.store + + i32.const 1 ;; stdout + i32.const 16 ;; iovs offset + i32.const 2 ;; iovsLen + i32.const 8 ;; nwritten + + call $fd_write + + i32.const 8 + i32.load + ) + + (func (export "badWrite") (result i32 i32 i32 i32) + i32.const 100000 ;; invalid descriptor + i32.const 8 ;; iovs offset + i32.const 1 ;; iovsLen + i32.const 4 ;; nwritten + + call $fd_write + + i32.const 1 ;; stdout + i32.const 65529 ;; bad iovs offset + i32.const 1 ;; iovsLen + i32.const 8 ;; nwritten + + call $fd_write + + i32.const 1 ;; stdout + i32.const 16 ;; iovs offset + i32.const 1 ;; iovsLen + i32.const 65533 ;; bad nwritten + + call $fd_write + + i32.const 1 ;; stdout + i32.const 8 ;; iovs offset + i32.const 8192 ;; bad iovsLen + i32.const 0 ;; nwritten + + call $fd_write + ) +) + +(assert_return (invoke "writeToStdout") (i32.const 0) (i32.const 13)) +(assert_return (invoke "badWrite") (i32.const 8) (i32.const 28) (i32.const 28) (i32.const 28))