Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement fd_fdstat_get #309

Merged
merged 1 commit into from
Dec 9, 2024
Merged
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
96 changes: 66 additions & 30 deletions src/wasi/WASI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,23 @@ static void* get_memory_pointer(Instance* instance, Value& value, size_t size)
return memory->buffer() + offset;
}

template <class T, int maxSize>
class TemporaryData {
public:
TemporaryData(size_t size)
{
/* Check that more memory is requested than the maximum provided by malloc. */
if (size > (std::numeric_limits<size_t>::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<T*>(malloc(size));
}
}

Expand All @@ -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)
Expand Down Expand Up @@ -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<size_t>(argv[2].asI32());
uint32_t* nwritten = reinterpret_cast<uint32_t*>(get_memory_pointer(instance, argv[3], sizeof(uint32_t)));
uint32_t* iovptr = reinterpret_cast<uint32_t*>(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<int16_t>(WasiErrNo::inval));
if (iovptr == nullptr || nwritten == nullptr) {
result[0] = Value(WasiErrNo::inval);
return;
}

std::vector<uvwasi_ciovec_t> iovs(iovcnt);
for (uint32_t i = 0; i < iovcnt; i++) {
iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + i * 8);
iovs[i].buf_len = *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
}
TemporaryData<uvwasi_ciovec_t, 8> 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<int16_t>(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<uvwasi_iovec_t> iovs(iovcnt);
for (uint32_t i = 0; i < iovcnt; i++) {
iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + i * 8);
iovs[i].buf_len = *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
size_t iovsLen = static_cast<size_t>(argv[2].asI32());
uint32_t* nread = reinterpret_cast<uint32_t*>(get_memory_pointer(instance, argv[3], sizeof(uint32_t)));
uint32_t* iovptr = reinterpret_cast<uint32_t*>(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<uvwasi_iovec_t, 8> 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<int16_t>(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<int16_t>(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<uvwasi_fdstat_t*>(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)
Expand Down Expand Up @@ -218,7 +254,7 @@ void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instan
return;
}

TemporaryData pointers(count * sizeof(void*));
TemporaryData<void*, 8> pointers(count);

if (pointers.data() == nullptr) {
result[0] = Value(WasiErrNo::inval);
Expand Down
2 changes: 2 additions & 0 deletions src/wasi/WASI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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);
Expand Down
60 changes: 60 additions & 0 deletions test/wasi/fd_fdstat_get.wast
Original file line number Diff line number Diff line change
@@ -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))

69 changes: 69 additions & 0 deletions test/wasi/print.wast
Original file line number Diff line number Diff line change
@@ -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))
Loading