From 32a6c3a5df93faa25ec68d17fa383de1b4dcc23f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 30 Mar 2020 05:45:32 -0700 Subject: [PATCH] Make `wasm_global_set` a safe API. Make `wasm_global_set` a safe API by allowing it to return a trap in error cases. Traps in the wasm spec are used to report runtime errors, and the errors `wasm_global_set` reports are things that wasm would catch at validation time. Nevertheless, using a trap here is consistent with the official JS API raising exceptions on the same conditions. And, it provides a message explaining the reason for the error. To support users for whom mutablility and type checks are believed to be unacceptable overhead, add a `wasm_global_set_unsafe` function which skips those checks, at the penalty of undefined behavior. --- example/global.c | 56 +++++++++++++++++++++++++++++++++++++++++++---- example/hostref.c | 3 ++- include/wasm.h | 9 +++++++- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/example/global.c b/example/global.c index 5fe357c..28f8ad6 100644 --- a/example/global.c +++ b/example/global.c @@ -44,6 +44,18 @@ wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { check(results[0], type, expected); \ } +#define check_no_trap(maybe_trap) \ + if ((maybe_trap)) { \ + printf("> Trap occurred\n"); \ + exit(1); \ + } + +#define check_trap(maybe_trap) \ + if (!(maybe_trap)) { \ + printf("> Expected trap did not occur\n"); \ + exit(1); \ + } + int main(int argc, const char* argv[]) { // Initialize. @@ -174,15 +186,31 @@ int main(int argc, const char* argv[]) { check_call(get_var_f32_export, f32, 7); check_call(get_var_i64_export, i64, 8); + // Attempt to mutate immutable globals. + wasm_val_t val33 = {.kind = WASM_F32, .of = {.f32 = 33}}; + check_trap(wasm_global_set(const_f32_import, &val33)); + check_global(const_f32_import, f32, 1); + wasm_val_t val34 = {.kind = WASM_I64, .of = {.i64 = 34}}; + check_trap(wasm_global_set(const_i64_import, &val34)); + check_global(const_i64_import, i64, 2); + + // Attempt to set globals with values of incorrect types. + wasm_val_t val37 = {.kind = WASM_F64, .of = {.f64 = 37}}; + check_trap(wasm_global_set(var_f32_export, &val37)); + check_global(var_f32_import, f32, 3); + wasm_val_t val38 = {.kind = WASM_I32, .of = {.i32 = 38}}; + check_trap(wasm_global_set(var_i64_export, &val38)); + check_global(var_i64_import, i64, 4); + // Modify variables through API and check again. wasm_val_t val33 = {.kind = WASM_F32, .of = {.f32 = 33}}; - wasm_global_set(var_f32_import, &val33); + check_no_trap(wasm_global_set(var_f32_import, &val33)); wasm_val_t val34 = {.kind = WASM_I64, .of = {.i64 = 34}}; - wasm_global_set(var_i64_import, &val34); + check_no_trap(wasm_global_set(var_i64_import, &val34)); wasm_val_t val37 = {.kind = WASM_F32, .of = {.f32 = 37}}; - wasm_global_set(var_f32_export, &val37); + check_no_trap(wasm_global_set(var_f32_export, &val37)); wasm_val_t val38 = {.kind = WASM_I64, .of = {.i64 = 38}}; - wasm_global_set(var_i64_export, &val38); + check_no_trap(wasm_global_set(var_i64_export, &val38)); check_global(var_f32_import, f32, 33); check_global(var_i64_import, i64, 34); @@ -194,6 +222,26 @@ int main(int argc, const char* argv[]) { check_call(get_var_f32_export, f32, 37); check_call(get_var_i64_export, i64, 38); + // Modify variables through unsafe API and check again. + wasm_val_t val33 = {.kind = WASM_F32, .of = {.f32 = 53}}; + wasm_global_set_unsafe(var_f32_import, &val33); + wasm_val_t val34 = {.kind = WASM_I64, .of = {.i64 = 54}}; + wasm_global_set_unsafe(var_i64_import, &val34); + wasm_val_t val37 = {.kind = WASM_F32, .of = {.f32 = 57}}; + wasm_global_set_unsafe(var_f32_export, &val37); + wasm_val_t val38 = {.kind = WASM_I64, .of = {.i64 = 58}}; + wasm_global_set_unsafe(var_i64_export, &val38); + + check_global(var_f32_import, f32, 53); + check_global(var_i64_import, i64, 54); + check_global(var_f32_export, f32, 57); + check_global(var_i64_export, i64, 58); + + check_call(get_var_f32_import, f32, 53); + check_call(get_var_i64_import, i64, 54); + check_call(get_var_f32_export, f32, 57); + check_call(get_var_i64_export, i64, 58); + // Modify variables through calls and check again. wasm_val_t args73[] = { {.kind = WASM_F32, .of = {.f32 = 73}} }; wasm_func_call(set_var_f32_import, args73, NULL); diff --git a/example/hostref.c b/example/hostref.c index b70218e..1b730b8 100644 --- a/example/hostref.c +++ b/example/hostref.c @@ -227,7 +227,8 @@ int main(int argc, const char* argv[]) { assert(val.kind == WASM_ANYREF); check(val.of.ref, NULL); val.of.ref = host2; - wasm_global_set(global, &val); + own wasm_trap_t *global_set_trap = wasm_global_set(global, &val); + assert(global_set_trap == NULL); check(call_v_r(global_get), host2); wasm_global_get(global, &val); assert(val.kind == WASM_ANYREF); diff --git a/include/wasm.h b/include/wasm.h index 46e75ca..b3beb45 100644 --- a/include/wasm.h +++ b/include/wasm.h @@ -436,7 +436,14 @@ WASM_API_EXTERN own wasm_global_t* wasm_global_new( WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*); WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out); -WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*); + +/// Perform a `global.set` operation. If the global is immutable or if the new +/// value has the wrong type, return an error. +WASM_API_EXTERN own wasm_trap_t* wasm_global_set(wasm_global_t*, const wasm_val_t*); + +/// Similar to `wasm_global_set`, except with undefined behavior if the global +/// is immutable or if the new value has the wrong type. +WASM_API_EXTERN void wasm_global_set_unsafe(wasm_global_t*, const wasm_val_t*); // Table Instances