Add bounds checking to operations involving arrays in sandbox memory

This commit is contained in:
刘皓 2025-08-10 11:27:39 -04:00
parent ae0e1bd6d5
commit 19fe3f955d
No known key found for this signature in database
GPG key ID: 7901753DB465B711
6 changed files with 82 additions and 13 deletions

View file

@ -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<char>(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);
}

View file

@ -196,6 +196,10 @@ namespace mkxp_sandbox {
return sandbox_ref<T>(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<T>(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;

View file

@ -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<uint8_t>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(2), (int32_t)1) - 1), SANDBOX_SLOT(2)));
std::reverse(&sb()->ref<uint8_t>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(2), (int32_t)1) - 1), &sb()->ref<uint8_t>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(2), (int32_t)1) - 1) + SANDBOX_SLOT(2));
SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref<uint8_t>(SANDBOX_SLOT(0), SANDBOX_SLOT(2) - 1), SANDBOX_SLOT(2)));
std::reverse(&sb()->ref<uint8_t>(SANDBOX_SLOT(0), SANDBOX_SLOT(2) - 1), &sb()->ref<uint8_t>(SANDBOX_SLOT(0), SANDBOX_SLOT(2) - 1) + SANDBOX_SLOT(2));
#else
SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref<uint8_t>(SANDBOX_SLOT(0)), SANDBOX_SLOT(2)));
SANDBOX_GUARD_L(bitmap->getRaw(sb().e, &sb()->ref<uint8_t>(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<Bitmap>(self)->replaceRaw(sb().e, &sb()->ref<uint8_t>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (int32_t)1) - 1), SANDBOX_SLOT(1)));
std::reverse(&sb()->ref<uint8_t>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (int32_t)1) - 1), &sb()->ref<uint8_t>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (int32_t)1) - 1) + SANDBOX_SLOT(1));
SANDBOX_GUARD_L(get_private_data<Bitmap>(self)->replaceRaw(sb().e, &sb()->ref<uint8_t>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), SANDBOX_SLOT(1)));
std::reverse(&sb()->ref<uint8_t>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref<uint8_t>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1) + SANDBOX_SLOT(1));
#else
SANDBOX_GUARD_L(get_private_data<Bitmap>(self)->replaceRaw(sb().e, &sb()->ref<uint8_t>(SANDBOX_SLOT(0)), SANDBOX_SLOT(1)));
SANDBOX_GUARD_L(get_private_data<Bitmap>(self)->replaceRaw(sb().e, &sb()->ref<uint8_t>(SANDBOX_SLOT(0)), SANDBOX_SLOT(1)));
#endif // MKXPZ_BIG_ENDIAN
}
}
return self;

View file

@ -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<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), &sb()->ref<char>(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<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), SANDBOX_SLOT(1))));
std::reverse(&sb()->ref<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), &sb()->ref<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1) + SANDBOX_SLOT(1));
std::reverse(&sb()->ref<char>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref<char>(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<char>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), SANDBOX_SLOT(1))));
std::reverse(&sb()->ref<char>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref<char>(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<char>(SANDBOX_SLOT(0)), SANDBOX_SLOT(1))));
SANDBOX_GUARD(set_private_data(sb().e, SANDBOX_SLOT(2), C::deserialize(sb().e, &sb()->ref<char>(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<C>(self)->serialize(&sb()->ref<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1));
std::reverse(&sb()->ref<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1), &sb()->ref<char>(SANDBOX_SLOT(0), std::max(SANDBOX_SLOT(1), (wasm_size_t)1) - 1) + SANDBOX_SLOT(1));
get_private_data<C>(self)->serialize(&sb()->ref<char>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1));
std::reverse(&sb()->ref<char>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1), &sb()->ref<char>(SANDBOX_SLOT(0), SANDBOX_SLOT(1) - 1) + SANDBOX_SLOT(1));
#else
get_private_data<C>(self)->serialize(&sb()->ref<char>(SANDBOX_SLOT(0)));
get_private_data<C>(self)->serialize(&sb()->ref<char>(SANDBOX_SLOT(0)));
#endif // MKXPZ_BIG_ENDIAN
}
}
return SANDBOX_SLOT(2);

View file

@ -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<uint32_t>(iovs);
uint32_t length = wasi->ref<uint32_t>(iovs + 4);
if (length > 0) {
wasi->check_bounds(ptr, length);
#ifdef MKXPZ_BIG_ENDIAN
uint8_t *buffer = &wasi->ref<uint8_t>(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<uint32_t>(iovs);
uint32_t length = wasi->ref<uint32_t>(iovs + 4);
if (length > 0) {
wasi->check_bounds(ptr, length);
#ifdef MKXPZ_BIG_ENDIAN
uint8_t *buffer = &wasi->ref<uint8_t>(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;

View file

@ -233,6 +233,10 @@ typedef struct w2c_wasi__snapshot__preview1 {
return ref<T>(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;