Remove exceptions from the sandbox implementation in libretro builds

This commit is contained in:
刘皓 2025-04-25 13:31:32 -04:00
parent d0b211e239
commit 0d07aff3e2
No known key found for this signature in database
GPG key ID: 7901753DB465B711
6 changed files with 84 additions and 108 deletions

View file

@ -83,7 +83,7 @@ wasm_ptr_t binding_base::sandbox_malloc(wasm_size_t size) {
// Verify that the entire allocated buffer is in valid memory
wasm_ptr_t buf_end;
if (buf == 0 || __builtin_add_overflow(buf, size, &buf_end) || buf_end >= instance().w2c_memory.size) {
if (buf == 0 || (buf_end = buf + size) < buf || buf_end >= instance().w2c_memory.size) {
return 0;
}

View file

@ -46,8 +46,8 @@ usize sandbox::sandbox_malloc(usize size) {
// Verify that the returned pointer is non-null and the entire allocated buffer is in valid memory
usize buf_end;
if (buf == WASM_NULL || __builtin_add_overflow(buf, size, &buf_end) || buf_end >= ruby->w2c_memory.size) {
throw SandboxOutOfMemoryException();
if (buf == WASM_NULL || (buf_end = buf + size) < buf || buf_end >= ruby->w2c_memory.size) {
throw std::bad_alloc();
}
return buf;
@ -58,83 +58,78 @@ void sandbox::sandbox_free(usize ptr) {
}
sandbox::sandbox() : ruby(new struct w2c_ruby), wasi(new wasi_t(ruby)), bindings(ruby), yielding(false), transitioning(false) {
try {
// Initialize the sandbox
wasm2c_ruby_instantiate(RB, wasi.get());
w2c_ruby_mkxp_sandbox_init(
RB,
0, // heap_free_slots
1.1, // growth_factor
0, // growth_max_slots
0, // heap_free_slots_min_ratio
0, // heap_free_slots_goal_ratio
0, // heap_free_slots_max_ratio
0, // uncollectible_wb_unprotected_objects_limit_ratio
0, // oldobject_limit_factor
1 * 0x100000, // malloc_limit_min
4 * 0x100000, // malloc_limit_max
1.1, // malloc_limit_growth_factor
4 * 0x100000, // oldmalloc_limit_min
8 * 0x100000, // oldmalloc_limit_max
1.1 // oldmalloc_limit_growth_factor
);
// Initialize the sandbox
wasm2c_ruby_instantiate(RB, wasi.get());
w2c_ruby_mkxp_sandbox_init(
RB,
0, // heap_free_slots
1.1, // growth_factor
0, // growth_max_slots
0, // heap_free_slots_min_ratio
0, // heap_free_slots_goal_ratio
0, // heap_free_slots_max_ratio
0, // uncollectible_wb_unprotected_objects_limit_ratio
0, // oldobject_limit_factor
1 * 0x100000, // malloc_limit_min
4 * 0x100000, // malloc_limit_max
1.1, // malloc_limit_growth_factor
4 * 0x100000, // oldmalloc_limit_min
8 * 0x100000, // oldmalloc_limit_max
1.1 // oldmalloc_limit_growth_factor
);
// Determine Ruby command-line arguments
std::vector<std::string> args{"mkxp-z"};
args.push_back("/mkxp-retro-dist/bin/mkxp-z");
if (MJIT_ENABLED) {
std::string verboseLevel("--mjit-verbose=");
std::string maxCache("--mjit-max-cache=");
std::string minCalls("--mjit-min-calls=");
args.push_back("--mjit");
verboseLevel += std::to_string(MJIT_VERBOSE);
maxCache += std::to_string(MJIT_MAX_CACHE);
minCalls += std::to_string(MJIT_MIN_CALLS);
args.push_back(verboseLevel.c_str());
args.push_back(maxCache.c_str());
args.push_back(minCalls.c_str());
} else if (YJIT_ENABLED) {
args.push_back("--yjit");
}
// Copy all the command-line arguments into the sandbox (sandboxed code can't access memory that's outside the sandbox!)
usize argv_buf = sandbox_malloc(args.size() * sizeof(usize));
for (usize i = 0; i < args.size(); ++i) {
usize arg_buf = sandbox_malloc(args[i].length() + 1);
std::strcpy((char *)WASM_MEM(arg_buf), args[i].c_str());
WASM_SET(usize, argv_buf + i * sizeof(usize), arg_buf);
}
// Pass the command-line arguments to Ruby
AWAIT(w2c_ruby_ruby_init_stack(RB, w2c_ruby_rb_wasm_get_stack_pointer(RB)));
AWAIT(w2c_ruby_ruby_init(RB));
usize node;
AWAIT(node = w2c_ruby_ruby_options(RB, args.size(), argv_buf));
// Start up Ruby executable node
bool valid;
u32 state;
usize state_buf = sandbox_malloc(sizeof(usize));
AWAIT(valid = w2c_ruby_ruby_executable_node(RB, node, state_buf));
if (valid) {
AWAIT(state = w2c_ruby_ruby_exec_node(RB, node));
}
if (!valid || state) {
throw SandboxNodeException();
}
sandbox_free(state_buf);
// Set the default encoding to UTF-8
usize encoding;
AWAIT(encoding = w2c_ruby_rb_utf8_encoding(RB));
usize enc;
AWAIT(enc = w2c_ruby_rb_enc_from_encoding(RB, encoding));
AWAIT(w2c_ruby_rb_enc_set_default_internal(RB, enc));
AWAIT(w2c_ruby_rb_enc_set_default_external(RB, enc));
} catch (SandboxException &) {
wasm2c_ruby_free(RB);
throw;
// Determine Ruby command-line arguments
std::vector<std::string> args{"mkxp-z"};
args.push_back("/mkxp-retro-dist/bin/mkxp-z");
if (MJIT_ENABLED) {
std::string verboseLevel("--mjit-verbose=");
std::string maxCache("--mjit-max-cache=");
std::string minCalls("--mjit-min-calls=");
args.push_back("--mjit");
verboseLevel += std::to_string(MJIT_VERBOSE);
maxCache += std::to_string(MJIT_MAX_CACHE);
minCalls += std::to_string(MJIT_MIN_CALLS);
args.push_back(verboseLevel.c_str());
args.push_back(maxCache.c_str());
args.push_back(minCalls.c_str());
} else if (YJIT_ENABLED) {
args.push_back("--yjit");
}
// Copy all the command-line arguments into the sandbox (sandboxed code can't access memory that's outside the sandbox!)
usize argv_buf = sandbox_malloc(args.size() * sizeof(usize));
for (usize i = 0; i < args.size(); ++i) {
usize arg_buf = sandbox_malloc(args[i].length() + 1);
std::strcpy((char *)WASM_MEM(arg_buf), args[i].c_str());
WASM_SET(usize, argv_buf + i * sizeof(usize), arg_buf);
}
// Pass the command-line arguments to Ruby
AWAIT(w2c_ruby_ruby_init_stack(RB, w2c_ruby_rb_wasm_get_stack_pointer(RB)));
AWAIT(w2c_ruby_ruby_init(RB));
usize node;
AWAIT(node = w2c_ruby_ruby_options(RB, args.size(), argv_buf));
// Start up Ruby executable node
bool valid;
u32 state;
usize state_buf = sandbox_malloc(sizeof(usize));
AWAIT(valid = w2c_ruby_ruby_executable_node(RB, node, state_buf));
if (valid) {
AWAIT(state = w2c_ruby_ruby_exec_node(RB, node));
}
if (!valid || state) {
std::abort();
}
sandbox_free(state_buf);
// Set the default encoding to UTF-8
usize encoding;
AWAIT(encoding = w2c_ruby_rb_utf8_encoding(RB));
usize enc;
AWAIT(enc = w2c_ruby_rb_enc_from_encoding(RB, encoding));
AWAIT(w2c_ruby_rb_enc_set_default_internal(RB, enc));
AWAIT(w2c_ruby_rb_enc_set_default_external(RB, enc));
}
sandbox::~sandbox() {

View file

@ -53,12 +53,4 @@ typedef float f32;
typedef double f64;
#endif // WASM_RT_CORE_TYPES_DEFINED
struct SandboxException {};
// The call to `ruby_executable_node()` or `ruby_exec_node()` failed when initializing Ruby.
struct SandboxNodeException : SandboxException {};
// Failed to allocate memory.
struct SandboxOutOfMemoryException : SandboxException {};
// An exception occurred inside of Ruby and was not caught.
struct SandboxTrapException : SandboxException {};
#endif // MKXPZ_SANDBOX_TYPES_H

View file

@ -805,7 +805,7 @@ extern "C" u32 w2c_wasi__snapshot__preview1_poll_oneoff(wasi_t *wasi, usize in,
extern "C" void w2c_wasi__snapshot__preview1_proc_exit(wasi_t *wasi, u32 rval) {
WASI_DEBUG("proc_exit(%u)\n", rval);
throw SandboxTrapException();
std::abort();
}
extern "C" u32 w2c_wasi__snapshot__preview1_random_get(wasi_t *wasi, usize buf, u32 buf_len) {

View file

@ -291,7 +291,7 @@ PRELUDE = <<~HEREDOC
bindings::rb_data_type::rb_data_type(wasm_ptr_t ptr) : ptr(ptr) {}
wasm_ptr_t bindings::rb_data_type::get() const {
if (ptr == 0) throw SandboxTrapException();
if (ptr == 0) std::abort();
return ptr;
}
@ -309,12 +309,12 @@ POSTSCRIPT = <<~HEREDOC
buf = sandbox_malloc(9 * sizeof(wasm_ptr_t));
if (buf == 0) {
throw SandboxOutOfMemoryException();
throw std::bad_alloc();
}
str = sandbox_malloc(std::strlen(wrap_struct_name) + 1);
if (str == 0) {
sandbox_free(buf);
throw SandboxOutOfMemoryException();
throw std::bad_alloc();
}
std::strcpy((char *)(**this + str), wrap_struct_name);
@ -480,7 +480,7 @@ File.readlines('tags', chomp: true).each do |line|
end
coroutine_initializer += <<~HEREDOC
default:
throw SandboxTrapException();
std::abort();
}
HEREDOC
else
@ -498,7 +498,7 @@ File.readlines('tags', chomp: true).each do |line|
elsif !handler[:buf_size].nil?
coroutine_initializer += <<~HEREDOC
f#{i} = bind.sandbox_malloc(#{handler[:buf_size].gsub('PREV_ARG', "a#{i - 1}").gsub('ARG', "a#{i}")});
if (f#{i} == 0) throw SandboxOutOfMemoryException();
if (f#{i} == 0) throw std::bad_alloc();
HEREDOC
coroutine_initializer += handler[:serialize].gsub('PREV_ARG', "a#{i - 1}").gsub('ARG', "a#{i}").gsub('BUF', "f#{i}")
coroutine_initializer += "\n"
@ -523,7 +523,7 @@ File.readlines('tags', chomp: true).each do |line|
fp = w2c_ruby_rb_wasm_get_stack_pointer(&bind.instance());
sp = fp - CEIL_WASMSTACKALIGN(a#{args.length - 2} * sizeof(VALUE));
if (sp > fp) {
throw SandboxOutOfMemoryException();
throw std::bad_alloc();
}
w2c_ruby_rb_wasm_set_stack_pointer(&bind.instance(), sp);
std::va_list a;
@ -550,7 +550,7 @@ File.readlines('tags', chomp: true).each do |line|
fp = w2c_ruby_rb_wasm_get_stack_pointer(&bind.instance());
sp = fp - CEIL_WASMSTACKALIGN(n * sizeof(VALUE));
if (sp > fp) {
throw SandboxOutOfMemoryException();
throw std::bad_alloc();
}
w2c_ruby_rb_wasm_set_stack_pointer(&bind.instance(), sp);
for (wasm_size_t i = 0; i < n; ++i) {

View file

@ -320,13 +320,7 @@ static bool init_sandbox() {
audio.emplace(*thread_data);
try {
mkxp_retro::sandbox.emplace();
} catch (SandboxException) {
log_printf(RETRO_LOG_ERROR, "Failed to initialize Ruby\n");
deinit_sandbox();
return false;
}
mkxp_retro::sandbox.emplace();
{
float refresh_rate;
@ -467,13 +461,8 @@ extern "C" RETRO_API void retro_run() {
}
if (should_render) {
try {
if (sb().run<struct main>()) {
log_printf(RETRO_LOG_INFO, "[Sandbox] Ruby terminated normally\n");
deinit_sandbox();
}
} catch (SandboxException) {
log_printf(RETRO_LOG_ERROR, "[Sandbox] Ruby threw an exception\n");
if (sb().run<struct main>()) {
log_printf(RETRO_LOG_INFO, "[Sandbox] Ruby terminated normally\n");
deinit_sandbox();
}
} else if (!dupe_supported && mkxp_retro::sandbox.has_value()) {