[wasm] Strong typing for object references

Such that wasm cannot crash us by passing wrong object refs.

https://github.com/bytecodealliance/wasm-micro-runtime/discussions/1987

It still is unsafe if some code in the process other than HarfBuzz
registers refs with wasm-micro-runtime, since wasm_externref_ref2obj()
takes no context variable and looks up refs globally :(.

Maybe I fix that later by keeping a hash table of ref->obj-type instead.
This commit is contained in:
Behdad Esfahbod 2023-02-23 11:53:08 -07:00
parent 6b72a18c7b
commit 747dcf561d
3 changed files with 55 additions and 29 deletions

View File

@ -24,22 +24,15 @@
#include "hb-wasm-api.hh" #include "hb-wasm-api.hh"
#define nullref 0
#define module_inst wasm_runtime_get_module_inst (exec_env) #define module_inst wasm_runtime_get_module_inst (exec_env)
#define HB_REF2OBJ(obj) \
hb_##obj##_t *obj = nullptr; \
(void) wasm_externref_ref2obj (obj##ref, (void **) &obj)
#define HB_OBJ2REF(obj) \
uint32_t obj##ref = nullref; \
(void) wasm_externref_obj2ref (module_inst, obj, &obj##ref)
#include "hb-wasm-api-font.hh" #include "hb-wasm-api-font.hh"
#undef nullref
#undef module_inst #undef module_inst
#undef HB_WASM_EXEC_ENV #undef HB_WASM_EXEC_ENV
#undef HB_REF2OBJ #undef HB_REF2OBJ
#undef HB_OBJ2REF #undef HB_OBJ2REF
hb_user_data_key_t _hb_wasm_ref_type_key = {};

View File

@ -42,4 +42,36 @@
#undef HB_WASM_END_DECLS #undef HB_WASM_END_DECLS
enum {
hb_wasm_ref_type_none,
hb_wasm_ref_type_face,
hb_wasm_ref_type_font,
hb_wasm_ref_type_buffer,
};
HB_INTERNAL extern hb_user_data_key_t _hb_wasm_ref_type_key;
#define nullref 0
#define HB_REF2OBJ(obj) \
hb_##obj##_t *obj = nullptr; \
HB_STMT_START { \
(void) wasm_externref_ref2obj (obj##ref, (void **) &obj); \
/* Check object type. */ \
/* This works because all our objects have the same hb_object_t layout. */ \
if (unlikely (hb_##obj##_get_user_data (obj, &_hb_wasm_ref_type_key) != \
(void *) hb_wasm_ref_type_##obj)) \
obj = nullptr; \
} HB_STMT_END
#define HB_OBJ2REF(obj) \
uint32_t obj##ref = nullref; \
HB_STMT_START { \
hb_##obj##_set_user_data (obj, &_hb_wasm_ref_type_key, \
(void *) hb_wasm_ref_type_##obj, \
nullptr, false); \
(void) wasm_externref_obj2ref (module_inst, obj, &obj##ref); \
} HB_STMT_END
#endif /* HB_WASM_API_HH */ #endif /* HB_WASM_API_HH */

View File

@ -67,7 +67,7 @@ init_wasm ()
init_args.native_module_name = "env"; init_args.native_module_name = "env";
init_args.native_symbols = _hb_wasm_native_symbols; init_args.native_symbols = _hb_wasm_native_symbols;
if (!wasm_runtime_full_init (&init_args)) if (unlikely (!wasm_runtime_full_init (&init_args)))
{ {
DEBUG_MSG (WASM, nullptr, "Init runtime environment failed."); DEBUG_MSG (WASM, nullptr, "Init runtime environment failed.");
return false; return false;
@ -95,7 +95,7 @@ _hb_wasm_shaper_face_data_create (hb_face_t *face)
wasm_module = wasm_runtime_load ((uint8_t *) hb_blob_get_data_writable (wasm_blob, nullptr), wasm_module = wasm_runtime_load ((uint8_t *) hb_blob_get_data_writable (wasm_blob, nullptr),
length, nullptr, 0); length, nullptr, 0);
if (!wasm_module) if (unlikely (!wasm_module))
{ {
DEBUG_MSG (WASM, nullptr, "Load wasm module failed."); DEBUG_MSG (WASM, nullptr, "Load wasm module failed.");
goto fail; goto fail;
@ -168,46 +168,47 @@ _hb_wasm_shape (hb_shape_plan_t *shape_plan,
module_inst = wasm_runtime_instantiate(face_data->wasm_module, module_inst = wasm_runtime_instantiate(face_data->wasm_module,
stack_size, heap_size, stack_size, heap_size,
nullptr, 0); nullptr, 0);
if (!module_inst) if (unlikely (!module_inst))
{ {
DEBUG_MSG (WASM, face_data->wasm_module, "Instantiate wasm module failed."); DEBUG_MSG (WASM, face_data->wasm_module, "Instantiate wasm module failed.");
return false;
}
// cmake -DWAMR_BUILD_REF_TYPES=1 for these to work
HB_OBJ2REF (font);
HB_OBJ2REF (buffer);
if (unlikely (!fontref || !bufferref))
{
DEBUG_MSG (WASM, module_inst, "Failed to register objects.");
goto fail; goto fail;
} }
exec_env = wasm_runtime_create_exec_env (module_inst, stack_size); exec_env = wasm_runtime_create_exec_env (module_inst, stack_size);
if (!exec_env) { if (unlikely (!exec_env)) {
DEBUG_MSG (WASM, module_inst, "Create wasm execution environment failed."); DEBUG_MSG (WASM, module_inst, "Create wasm execution environment failed.");
goto fail; goto fail;
} }
if (!(shape_func = wasm_runtime_lookup_function (module_inst, "shape", nullptr))) shape_func = wasm_runtime_lookup_function (module_inst, "shape", nullptr);
if (unlikely (!shape_func))
{ {
DEBUG_MSG (WASM, module_inst, "Shape function not found."); DEBUG_MSG (WASM, module_inst, "Shape function not found.");
goto fail; goto fail;
} }
uint32_t font_ref;
uint32_t buffer_ref;
// cmake -DWAMR_BUILD_REF_TYPES=1 for these to work
if (!wasm_externref_obj2ref (module_inst, font, &font_ref) ||
!wasm_externref_obj2ref (module_inst, buffer, &buffer_ref))
{
DEBUG_MSG (WASM, module_inst, "Failed to register objects.");
goto fail;
}
wasm_val_t results[1]; wasm_val_t results[1];
wasm_val_t arguments[2]; wasm_val_t arguments[2];
results[0].kind = WASM_I32; results[0].kind = WASM_I32;
arguments[0].kind = WASM_I32; arguments[0].kind = WASM_I32;
arguments[0].of.i32 = font_ref; arguments[0].of.i32 = fontref;
arguments[1].kind = WASM_I32; arguments[1].kind = WASM_I32;
arguments[1].of.i32 = buffer_ref; arguments[1].of.i32 = bufferref;
if (!wasm_runtime_call_wasm_a (exec_env, shape_func, ret = wasm_runtime_call_wasm_a (exec_env, shape_func,
ARRAY_LENGTH (results), results, ARRAY_LENGTH (results), results,
ARRAY_LENGTH (arguments), arguments)) ARRAY_LENGTH (arguments), arguments);
if (unlikely (!ret))
{ {
DEBUG_MSG (WASM, module_inst, "Calling shape function failed: %s", DEBUG_MSG (WASM, module_inst, "Calling shape function failed: %s",
wasm_runtime_get_exception(module_inst)); wasm_runtime_get_exception(module_inst));