Skip to content

Commit 30d2433

Browse files
liamappelbecommit-bot@chromium.org
authored andcommitted
[vm] Fix all wasm exception memory leaks
This involved refactoring so that all the throws happen in the top level native entry functions, and then making sure that all the native objects are accounted for in those locations. I ran the wasm tests under asan and verified they used to leak but now don't. Bug: #37882 Change-Id: I9991ff6b9f8af1fa2f335c73ecf11aba29ea8c72 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117443 Commit-Queue: Liam Appelbe <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent bab399b commit 30d2433

File tree

1 file changed

+104
-45
lines changed

1 file changed

+104
-45
lines changed

runtime/lib/wasm.cc

Lines changed: 104 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@
1616

1717
namespace dart {
1818

19-
static void ThrowIfFailed(wasmer_result_t status) {
20-
if (status == wasmer_result_t::WASMER_OK) return;
21-
int len = wasmer_last_error_length();
22-
auto error = std::unique_ptr<char[]>(new char[len]);
23-
int read_len = wasmer_last_error_message(error.get(), len);
24-
ASSERT(read_len == len);
19+
static void ThrowWasmerError() {
2520
TransitionNativeToVM transition(Thread::Current());
26-
Exceptions::ThrowArgumentError(
27-
String::Handle(String::NewFormatted("Wasmer error: %s", error.get())));
21+
String& error = String::Handle();
22+
{
23+
int len = wasmer_last_error_length();
24+
auto raw_error = std::unique_ptr<char[]>(new char[len]);
25+
int read_len = wasmer_last_error_message(raw_error.get(), len);
26+
ASSERT(read_len == len);
27+
error = String::NewFormatted("Wasmer error: %s", raw_error.get());
28+
}
29+
Exceptions::ThrowArgumentError(error);
2830
}
2931

3032
template <typename T>
@@ -181,11 +183,10 @@ class WasmFunction {
181183
return dart_ret == _ret;
182184
}
183185

184-
wasmer_value_t Call(const wasmer_value_t* params) {
185-
wasmer_value_t result;
186-
ThrowIfFailed(wasmer_export_func_call(_fn, params, _args.length(), &result,
187-
IsVoid() ? 0 : 1));
188-
return result;
186+
bool Call(const wasmer_value_t* params, wasmer_value_t* result) {
187+
return wasmer_export_func_call(_fn, params, _args.length(), result,
188+
IsVoid() ? 0 : 1) ==
189+
wasmer_result_t::WASMER_OK;
189190
}
190191

191192
void Print(std::ostream& o, const char* name) const {
@@ -226,20 +227,29 @@ class WasmFunction {
226227

227228
class WasmInstance {
228229
public:
229-
explicit WasmInstance(wasmer_module_t* module, WasmImports* imports) {
230+
WasmInstance() : _instance(nullptr), _exports(nullptr) {}
231+
232+
bool Instantiate(wasmer_module_t* module, WasmImports* imports) {
230233
// Instantiate module.
231-
ThrowIfFailed(wasmer_module_instantiate(
232-
module, &_instance, imports->RawImports(), imports->NumImports()));
234+
if (wasmer_module_instantiate(module, &_instance, imports->RawImports(),
235+
imports->NumImports()) !=
236+
wasmer_result_t::WASMER_OK) {
237+
return false;
238+
}
233239

234240
// Load all functions.
235241
wasmer_instance_exports(_instance, &_exports);
236242
intptr_t num_exports = wasmer_exports_len(_exports);
237243
for (intptr_t i = 0; i < num_exports; ++i) {
238244
wasmer_export_t* exp = wasmer_exports_get(_exports, i);
239245
if (wasmer_export_kind(exp) == wasmer_import_export_kind::WASM_FUNCTION) {
240-
AddFunction(exp);
246+
if (!AddFunction(exp)) {
247+
return false;
248+
}
241249
}
242250
}
251+
252+
return true;
243253
}
244254

245255
~WasmInstance() {
@@ -248,25 +258,31 @@ class WasmInstance {
248258
delete[] kv->key;
249259
delete kv->value;
250260
}
251-
wasmer_exports_destroy(_exports);
252-
wasmer_instance_destroy(_instance);
261+
if (_exports != nullptr) {
262+
wasmer_exports_destroy(_exports);
263+
}
264+
if (_instance != nullptr) {
265+
wasmer_instance_destroy(_instance);
266+
}
253267
}
254268

255269
WasmFunction* GetFunction(const char* name,
256270
const MallocGrowableArray<classid_t>& dart_args,
257-
classid_t dart_ret) {
271+
classid_t dart_ret,
272+
String* error) {
258273
WasmFunction* fn = _functions.LookupValue(name);
259274
if (fn == nullptr) {
260-
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
275+
*error = String::NewFormatted(
261276
"Couldn't find a function called %s in the WASM module's exports",
262-
name)));
277+
name);
263278
return nullptr;
264279
}
265280
if (!fn->SignatureMatches(dart_args, dart_ret)) {
266281
std::stringstream sig;
267282
fn->Print(sig, name);
268-
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
269-
"Function signature doesn't match: %s", sig.str().c_str())));
283+
*error = String::NewFormatted("Function signature doesn't match: %s",
284+
sig.str().c_str());
285+
return nullptr;
270286
}
271287
return fn;
272288
}
@@ -301,21 +317,33 @@ class WasmInstance {
301317
return 0;
302318
}
303319

304-
void AddFunction(wasmer_export_t* exp) {
320+
bool AddFunction(wasmer_export_t* exp) {
305321
const wasmer_export_func_t* fn = wasmer_export_to_func(exp);
306322

307323
uint32_t num_rets;
308-
ThrowIfFailed(wasmer_export_func_returns_arity(fn, &num_rets));
324+
if (wasmer_export_func_returns_arity(fn, &num_rets) !=
325+
wasmer_result_t::WASMER_OK) {
326+
return false;
327+
}
309328
ASSERT(num_rets <= 1);
310329
wasmer_value_tag wasm_ret;
311-
ThrowIfFailed(wasmer_export_func_returns(fn, &wasm_ret, num_rets));
330+
if (wasmer_export_func_returns(fn, &wasm_ret, num_rets) !=
331+
wasmer_result_t::WASMER_OK) {
332+
return false;
333+
}
312334
classid_t ret = num_rets == 0 ? kWasmVoidCid : ToDartType(wasm_ret);
313335

314336
uint32_t num_args;
315-
ThrowIfFailed(wasmer_export_func_params_arity(fn, &num_args));
337+
if (wasmer_export_func_params_arity(fn, &num_args) !=
338+
wasmer_result_t::WASMER_OK) {
339+
return false;
340+
}
316341
auto wasm_args =
317342
std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]);
318-
ThrowIfFailed(wasmer_export_func_params(fn, wasm_args.get(), num_args));
343+
if (wasmer_export_func_params(fn, wasm_args.get(), num_args) !=
344+
wasmer_result_t::WASMER_OK) {
345+
return false;
346+
}
319347
MallocGrowableArray<classid_t> args;
320348
for (intptr_t i = 0; i < num_args; ++i) {
321349
args.Add(ToDartType(wasm_args[i]));
@@ -329,6 +357,7 @@ class WasmInstance {
329357
name[name_bytes.bytes_len] = '\0';
330358

331359
_functions.Insert({name, new WasmFunction(std::move(args), ret, fn)});
360+
return true;
332361
}
333362

334363
DISALLOW_COPY_AND_ASSIGN(WasmInstance);
@@ -353,7 +382,11 @@ DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) {
353382
wasmer_module_t* module;
354383
{
355384
TransitionVMToNative transition(thread);
356-
ThrowIfFailed(wasmer_compile(&module, data_copy.get(), len));
385+
if (wasmer_compile(&module, data_copy.get(), len) !=
386+
wasmer_result_t::WASMER_OK) {
387+
data_copy.reset();
388+
ThrowWasmerError();
389+
}
357390
}
358391

359392
mod_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(module));
@@ -436,7 +469,9 @@ DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
436469
descriptor.max.has_some = true;
437470
descriptor.max.some = max_size;
438471
}
439-
ThrowIfFailed(wasmer_memory_new(&memory, descriptor));
472+
if (wasmer_memory_new(&memory, descriptor) != wasmer_result_t::WASMER_OK) {
473+
ThrowWasmerError();
474+
}
440475
mem_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(memory));
441476
FinalizablePersistentHandle::New(thread->isolate(), mem_wrap, memory,
442477
FinalizeWasmMemory, init_size);
@@ -451,7 +486,10 @@ DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 2) {
451486

452487
wasmer_memory_t* memory =
453488
reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0));
454-
ThrowIfFailed(wasmer_memory_grow(memory, delta.AsInt64Value()));
489+
if (wasmer_memory_grow(memory, delta.AsInt64Value()) !=
490+
wasmer_result_t::WASMER_OK) {
491+
ThrowWasmerError();
492+
}
455493
return WasmMemoryToExternalTypedData(memory);
456494
}
457495

@@ -469,10 +507,17 @@ DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) {
469507
WasmImports* imports =
470508
reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
471509

472-
WasmInstance* inst;
510+
WasmInstance* inst = nullptr;
473511
{
474512
TransitionVMToNative transition(thread);
475-
inst = new WasmInstance(module, imports);
513+
inst = new WasmInstance();
514+
if (!inst->Instantiate(module, imports)) {
515+
delete inst;
516+
inst = nullptr;
517+
}
518+
}
519+
if (inst == nullptr) {
520+
ThrowWasmerError();
476521
}
477522

478523
inst_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(inst));
@@ -495,17 +540,27 @@ DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) {
495540
WasmInstance* inst =
496541
reinterpret_cast<WasmInstance*>(inst_wrap.GetNativeField(0));
497542

498-
Function& sig = Function::Handle(fn_type.signature());
499-
Array& args = Array::Handle(sig.parameter_types());
500-
MallocGrowableArray<classid_t> dart_args;
501-
for (intptr_t i = sig.NumImplicitParameters(); i < args.Length(); ++i) {
502-
dart_args.Add(
503-
AbstractType::Cast(Object::Handle(args.At(i))).type_class_id());
543+
WasmFunction* fn;
544+
String& error = String::Handle(zone);
545+
546+
{
547+
Function& sig = Function::Handle(fn_type.signature());
548+
Array& args = Array::Handle(sig.parameter_types());
549+
MallocGrowableArray<classid_t> dart_args;
550+
for (intptr_t i = sig.NumImplicitParameters(); i < args.Length(); ++i) {
551+
dart_args.Add(
552+
AbstractType::Cast(Object::Handle(args.At(i))).type_class_id());
553+
}
554+
classid_t dart_ret =
555+
AbstractType::Handle(sig.result_type()).type_class_id();
556+
557+
std::unique_ptr<char[]> name_raw = ToUTF8(name);
558+
fn = inst->GetFunction(name_raw.get(), dart_args, dart_ret, &error);
504559
}
505-
classid_t dart_ret = AbstractType::Handle(sig.result_type()).type_class_id();
506560

507-
std::unique_ptr<char[]> name_raw = ToUTF8(name);
508-
WasmFunction* fn = inst->GetFunction(name_raw.get(), dart_args, dart_ret);
561+
if (fn == nullptr) {
562+
Exceptions::ThrowArgumentError(error);
563+
}
509564

510565
fn_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(fn));
511566
// Don't need a finalizer because WasmFunctions are owned their WasmInstance.
@@ -535,15 +590,19 @@ DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) {
535590
for (intptr_t i = 0; i < args.Length(); ++i) {
536591
if (!ToWasmValue(Number::Cast(Object::Handle(args.At(i))), fn->args()[i],
537592
&params[i])) {
593+
params.reset();
538594
Exceptions::ThrowArgumentError(String::Handle(
539595
String::NewFormatted("Arg %" Pd " is the wrong type.", i)));
540596
}
541597
}
542598

543599
wasmer_value_t ret;
544600
{
545-
TransitionVMToNative transition(Thread::Current());
546-
ret = fn->Call(params.get());
601+
TransitionVMToNative transition(thread);
602+
if (!fn->Call(params.get(), &ret)) {
603+
params.reset();
604+
ThrowWasmerError();
605+
}
547606
}
548607
return fn->IsVoid() ? Object::null() : ToDartObject(ret);
549608
}

0 commit comments

Comments
 (0)