diff --git a/binding-sandbox/binding-base.cpp b/binding-sandbox/binding-base.cpp index 901af223..e56d248e 100644 --- a/binding-sandbox/binding-base.cpp +++ b/binding-sandbox/binding-base.cpp @@ -126,6 +126,12 @@ void binding_base::copy_memory_from(const void *ptr, wasm_size_t size, wasm_size } } +void mkxp_sandbox::sandbox_check_bounds(struct w2c_ruby &instance, wasm_ptr_t address, wasm_size_t size) noexcept { + if (address >= instance.w2c_memory.size || instance.w2c_memory.size - address < size) { + std::abort(); + } +} + wasm_size_t mkxp_sandbox::sandbox_strlen(struct w2c_ruby &instance, wasm_ptr_t address) noexcept { const char *ptr = &sandbox_ref(instance, address); #ifdef MKXPZ_BIG_ENDIAN @@ -171,6 +177,10 @@ void mkxp_sandbox::sandbox_strncpy_s(struct w2c_ruby &instance, wasm_ptr_t dst_a sandbox_arycpy(instance, dst_address, src, std::min((wasm_size_t)std::strlen(src) + 1, max_size)); } +void binding_base::check_bounds(wasm_ptr_t address, wasm_size_t size) const noexcept { + return sandbox_check_bounds(instance(), address, size); +} + wasm_size_t binding_base::strlen(wasm_ptr_t address) const noexcept { return sandbox_strlen(instance(), address); } diff --git a/binding-sandbox/binding-base.h b/binding-sandbox/binding-base.h index f15af664..bbeaa30c 100644 --- a/binding-sandbox/binding-base.h +++ b/binding-sandbox/binding-base.h @@ -196,6 +196,10 @@ namespace mkxp_sandbox { return sandbox_ref(array_address + array_index * sizeof(T)); } + // Checks if the array with the given address and size in bytes is within the bounds of sandbox memory. + // If it isn't, aborts. Otherwise, does nothing. + void sandbox_check_bounds(struct w2c_ruby &instance, wasm_ptr_t address, wasm_size_t size) noexcept; + // Gets the length of a string stored at a given address in sandbox memory. wasm_size_t sandbox_strlen(struct w2c_ruby &instance, wasm_ptr_t address) noexcept; @@ -387,6 +391,10 @@ namespace mkxp_sandbox { return ref(array_address + array_index * sizeof(T)); } + // Checks if the array with the given address and size in bytes is within the bounds of sandbox memory. + // If it isn't, aborts. Otherwise, does nothing. + void check_bounds(wasm_ptr_t address, wasm_size_t size) const noexcept; + // Gets the length of a string stored at a given address in sandbox memory. wasm_size_t strlen(wasm_ptr_t address) const noexcept; diff --git a/binding-sandbox/bitmap-binding.cpp b/binding-sandbox/bitmap-binding.cpp index 4df54393..379111d9 100644 --- a/binding-sandbox/bitmap-binding.cpp +++ b/binding-sandbox/bitmap-binding.cpp @@ -407,12 +407,15 @@ static VALUE get_raw_data(VALUE self) { SANDBOX_AWAIT_S(1, rb_str_new_cstr, ""); SANDBOX_AWAIT(rb_str_resize, SANDBOX_SLOT(1), SANDBOX_SLOT(2)); SANDBOX_AWAIT_S(0, rb_string_value_ptr, &SANDBOX_SLOT(1)); + if (SANDBOX_SLOT(2) > 0) { + sb()->check_bounds(SANDBOX_SLOT(0), SANDBOX_SLOT(2)); #ifdef MKXPZ_BIG_ENDIAN - SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(2), (int32_t)1) - 1), SANDBOX_SLOT(2))); - std::reverse(&sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(2), (int32_t)1) - 1), &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(2), (int32_t)1) - 1) + SANDBOX_SLOT(2)); + SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(2) - 1), SANDBOX_SLOT(2))); + std::reverse(&sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(2) - 1), &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(2) - 1) + SANDBOX_SLOT(2)); #else - SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0)), SANDBOX_SLOT(2))); + SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0)), SANDBOX_SLOT(2))); #endif // MKXPZ_BIG_ENDIAN + } } return SANDBOX_SLOT(1); @@ -430,12 +433,15 @@ static VALUE set_raw_data(VALUE self, VALUE value) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_ptr, &value); SANDBOX_AWAIT_S(1, get_bytesize, value); + if (SANDBOX_SLOT(1) > 0) { + sb()->check_bounds(SANDBOX_SLOT(0), SANDBOX_SLOT(1)); #ifdef MKXPZ_BIG_ENDIAN - SANDBOX_GUARD_L(get_private_data(self)->replaceRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (int32_t)1) - 1), SANDBOX_SLOT(1))); - std::reverse(&sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (int32_t)1) - 1), &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (int32_t)1) - 1) + SANDBOX_SLOT(1)); + SANDBOX_GUARD_L(get_private_data(self)->replaceRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), SANDBOX_SLOT(1))); + std::reverse(&sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1) + SANDBOX_SLOT(1)); #else - SANDBOX_GUARD_L(get_private_data(self)->replaceRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0)), SANDBOX_SLOT(1))); + SANDBOX_GUARD_L(get_private_data(self)->replaceRaw(sb().e, &sb()->ref(SANDBOX_SLOT(0)), SANDBOX_SLOT(1))); #endif // MKXPZ_BIG_ENDIAN + } } return self; diff --git a/binding-sandbox/serializable-binding.h b/binding-sandbox/serializable-binding.h index 1d47a51e..85acce1c 100644 --- a/binding-sandbox/serializable-binding.h +++ b/binding-sandbox/serializable-binding.h @@ -35,13 +35,16 @@ namespace mkxp_sandbox { SANDBOX_AWAIT_S(2, rb_obj_alloc, klass); SANDBOX_AWAIT_S(0, rb_string_value_ptr, &src); SANDBOX_AWAIT_S(1, get_bytesize, src); + if (SANDBOX_SLOT(1) > 0) { + sb()->check_bounds(SANDBOX_SLOT(0), SANDBOX_SLOT(1)); #ifdef MKXPZ_BIG_ENDIAN - std::reverse(&sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1) + SANDBOX_SLOT(1)); - SANDBOX_GUARD(set_private_data(sb().e, SANDBOX_SLOT(2), C::deserialize(sb().e, &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), SANDBOX_SLOT(1)))); - std::reverse(&sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1) + SANDBOX_SLOT(1)); + std::reverse(&sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1) + SANDBOX_SLOT(1)); + SANDBOX_GUARD(set_private_data(sb().e, SANDBOX_SLOT(2), C::deserialize(sb().e, &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), SANDBOX_SLOT(1)))); + std::reverse(&sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1) + SANDBOX_SLOT(1)); #else - SANDBOX_GUARD(set_private_data(sb().e, SANDBOX_SLOT(2), C::deserialize(sb().e, &sb()->ref(SANDBOX_SLOT(0)), SANDBOX_SLOT(1)))); + SANDBOX_GUARD(set_private_data(sb().e, SANDBOX_SLOT(2), C::deserialize(sb().e, &sb()->ref(SANDBOX_SLOT(0)), SANDBOX_SLOT(1)))); #endif // MKXPZ_BIG_ENDIAN + } } return SANDBOX_SLOT(2); @@ -61,12 +64,15 @@ namespace mkxp_sandbox { SANDBOX_AWAIT_S(2, rb_str_new_cstr, ""); SANDBOX_AWAIT(rb_str_resize, SANDBOX_SLOT(2), SANDBOX_SLOT(1)); SANDBOX_AWAIT_S(0, rb_string_value_ptr, &SANDBOX_SLOT(2)); + if (SANDBOX_SLOT(1) > 0) { + sb()->check_bounds(SANDBOX_SLOT(0), SANDBOX_SLOT(1)); #ifdef MKXPZ_BIG_ENDIAN - get_private_data(self)->serialize(&sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1)); - std::reverse(&sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), &sb()->ref(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1) + SANDBOX_SLOT(1)); + get_private_data(self)->serialize(&sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1)); + std::reverse(&sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1) + SANDBOX_SLOT(1)); #else - get_private_data(self)->serialize(&sb()->ref(SANDBOX_SLOT(0))); + get_private_data(self)->serialize(&sb()->ref(SANDBOX_SLOT(0))); #endif // MKXPZ_BIG_ENDIAN + } } return SANDBOX_SLOT(2); diff --git a/binding-sandbox/wasi.cpp b/binding-sandbox/wasi.cpp index 94fea742..ac255649 100644 --- a/binding-sandbox/wasi.cpp +++ b/binding-sandbox/wasi.cpp @@ -138,6 +138,10 @@ void wasi_t::deallocate_file_descriptor(uint32_t fd) { } } +void wasi_t::check_bounds(mkxp_sandbox::wasm_ptr_t address, mkxp_sandbox::wasm_size_t size) const noexcept { + sandbox_check_bounds(*ruby, address, size); +} + wasm_size_t wasi_t::strlen(wasm_ptr_t address) const noexcept { return sandbox_strlen(*ruby, address); } @@ -541,6 +545,12 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_filestat_set_size(wasi_t *wa extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_pread(wasi_t *wasi, uint32_t fd, wasm_ptr_t iovs, uint32_t iovs_len, uint64_t offset, wasm_ptr_t result) { WASI_DEBUG("fd_pread(%u, 0x%08x (%u), %lu)\n", fd, iovs, iovs_len, offset); + + if (8 * (wasm_size_t)iovs_len <= (wasm_size_t)iovs_len) { + std::abort(); + } + wasi->check_bounds(iovs, 8 * (wasm_size_t)iovs_len); + return WASI_ENOSYS; } @@ -599,12 +609,23 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_prestat_get(wasi_t *wasi, ui extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_pwrite(wasi_t *wasi, uint32_t fd, wasm_ptr_t iovs, uint32_t iovs_len, uint64_t offset, wasm_ptr_t result) { WASI_DEBUG("fd_pwrite(%u, 0x%08x (%u), %lu)\n", fd, iovs, iovs_len, offset); + + if (8 * (wasm_size_t)iovs_len <= (wasm_size_t)iovs_len) { + std::abort(); + } + wasi->check_bounds(iovs, 8 * (wasm_size_t)iovs_len); + return WASI_ENOSYS; } extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_read(wasi_t *wasi, uint32_t fd, wasm_ptr_t iovs, uint32_t iovs_len, wasm_ptr_t result) { WASI_DEBUG("fd_read(%u, 0x%08x (%u))\n", fd, iovs, iovs_len); + if (8 * (wasm_size_t)iovs_len <= (wasm_size_t)iovs_len) { + std::abort(); + } + wasi->check_bounds(iovs, 8 * (wasm_size_t)iovs_len); + if (fd >= wasi->fdtable.size()) { return WASI_EBADF; } @@ -630,6 +651,7 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_read(wasi_t *wasi, uint32_t uint32_t ptr = wasi->ref(iovs); uint32_t length = wasi->ref(iovs + 4); if (length > 0) { + wasi->check_bounds(ptr, length); #ifdef MKXPZ_BIG_ENDIAN uint8_t *buffer = &wasi->ref(ptr, length - 1); #else @@ -656,6 +678,8 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_read(wasi_t *wasi, uint32_t extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_readdir(wasi_t *wasi, uint32_t fd, wasm_ptr_t buf, uint32_t buf_len, uint64_t cookie, wasm_ptr_t result) { WASI_DEBUG("fd_readdir(%u, 0x%08x (%u), %lu)\n", fd, buf, buf_len, cookie); + wasi->check_bounds(buf, buf_len); + if (fd >= wasi->fdtable.size()) { return WASI_EBADF; } @@ -850,6 +874,11 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_tell(wasi_t *wasi, uint32_t extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_write(wasi_t *wasi, uint32_t fd, wasm_ptr_t iovs, uint32_t iovs_len, wasm_ptr_t result) { WASI_DEBUG("fd_write(%u, 0x%08x (%u))\n", fd, iovs, iovs_len); + if (8 * (wasm_size_t)iovs_len <= (wasm_size_t)iovs_len) { + std::abort(); + } + wasi->check_bounds(iovs, 8 * (wasm_size_t)iovs_len); + if (fd >= wasi->fdtable.size()) { return WASI_EBADF; } @@ -897,6 +926,7 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_write(wasi_t *wasi, uint32_t uint32_t ptr = wasi->ref(iovs); uint32_t length = wasi->ref(iovs + 4); if (length > 0) { + wasi->check_bounds(ptr, length); #ifdef MKXPZ_BIG_ENDIAN uint8_t *buffer = &wasi->ref(ptr, length - 1); std::reverse(buffer, buffer + length); @@ -1102,6 +1132,9 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_path_open(wasi_t *wasi, uint32_ extern "C" uint32_t w2c_wasi__snapshot__preview1_path_readlink(wasi_t *wasi, uint32_t fd, wasm_ptr_t path, uint32_t path_len, wasm_ptr_t buf, uint32_t buf_len, wasm_ptr_t result) { WASI_DEBUG("path_readlink(%u, \"%.*s\", 0x%08x (%u))\n", fd, path_len, (const char *)wasi->str(path), buf, buf_len); + + wasi->check_bounds(buf, buf_len); + return WASI_ENOSYS; } @@ -1260,6 +1293,8 @@ extern "C" void w2c_wasi__snapshot__preview1_proc_exit(wasi_t *wasi, uint32_t rv extern "C" uint32_t w2c_wasi__snapshot__preview1_random_get(wasi_t *wasi, wasm_ptr_t buf, uint32_t buf_len) { WASI_DEBUG("random_get(0x%08x (%u))\n", buf, buf_len); + wasi->check_bounds(buf, buf_len); + while (buf_len > 0) { if (wasi->prng_buffer_size == 0) { wasi->prng_buffer_size = 4; diff --git a/binding-sandbox/wasi.h b/binding-sandbox/wasi.h index 7c6a0bd1..30b78b17 100644 --- a/binding-sandbox/wasi.h +++ b/binding-sandbox/wasi.h @@ -233,6 +233,10 @@ typedef struct w2c_wasi__snapshot__preview1 { return ref(array_address + array_index * sizeof(T)); } + // Checks if the array with the given address and size in bytes is within the bounds of sandbox memory. + // If it isn't, aborts. Otherwise, does nothing. + void check_bounds(mkxp_sandbox::wasm_ptr_t address, mkxp_sandbox::wasm_size_t size) const noexcept; + // Gets a string stored at a given address in sandbox memory. struct mkxp_sandbox::sandbox_str_guard str(mkxp_sandbox::wasm_ptr_t address) const noexcept;