diff --git a/bazel/external/v8.patch b/bazel/external/v8.patch new file mode 100644 index 000000000..b13fad255 --- /dev/null +++ b/bazel/external/v8.patch @@ -0,0 +1,15 @@ +# Disable pointer compression (limits the maximum number of WasmVMs). + +diff --git a/BUILD.bazel b/BUILD.bazel +index 1cc0121e60..4947c1dba2 100644 +--- a/BUILD.bazel ++++ b/BUILD.bazel +@@ -161,7 +161,7 @@ v8_int( + # If no explicit value for v8_enable_pointer_compression, we set it to 'none'. + v8_string( + name = "v8_enable_pointer_compression", +- default = "none", ++ default = "False", + ) + + # Default setting for v8_enable_pointer_compression. diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 2970b6936..bef81aa7b 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -98,6 +98,8 @@ def proxy_wasm_cpp_host_repositories(): commit = "90f089d97b6e4146ad106eee1829d86ad6392027", remote = "https://chromium.googlesource.com/v8/v8", shallow_since = "1643043727 +0000", + patches = ["@proxy_wasm_cpp_host//bazel/external:v8.patch"], + patch_args = ["-p1"], ) native.bind( diff --git a/src/v8/v8.cc b/src/v8/v8.cc index 057c364ca..1c0ae6932 100644 --- a/src/v8/v8.cc +++ b/src/v8/v8.cc @@ -250,25 +250,32 @@ template constexpr T convertValTypesToArgsTuple(const U bool V8::load(std::string_view bytecode, std::string_view precompiled, const std::unordered_map function_names) { store_ = wasm::Store::make(engine()); + if (store_ == nullptr) { + return false; + } if (!precompiled.empty()) { auto vec = wasm::vec::make_uninitialized(precompiled.size()); ::memcpy(vec.get(), precompiled.data(), precompiled.size()); module_ = wasm::Module::deserialize(store_.get(), vec); + if (module_ == nullptr) { + return false; + } } else { auto vec = wasm::vec::make_uninitialized(bytecode.size()); ::memcpy(vec.get(), bytecode.data(), bytecode.size()); module_ = wasm::Module::make(store_.get(), vec); + if (module_ == nullptr) { + return false; + } } - if (!module_) { + shared_module_ = module_->share(); + if (shared_module_ == nullptr) { return false; } - shared_module_ = module_->share(); - assert(shared_module_ != nullptr); - function_names_index_ = function_names; return true; @@ -278,10 +285,26 @@ std::unique_ptr V8::clone() { assert(shared_module_ != nullptr); auto clone = std::make_unique(); - clone->integration().reset(integration()->clone()); + if (clone == nullptr) { + return nullptr; + } + clone->store_ = wasm::Store::make(engine()); + if (clone->store_ == nullptr) { + return nullptr; + } clone->module_ = wasm::Module::obtain(clone->store_.get(), shared_module_.get()); + if (clone->module_ == nullptr) { + return nullptr; + } + + auto integration_clone = integration()->clone(); + if (integration_clone == nullptr) { + return nullptr; + } + clone->integration().reset(integration_clone); + clone->function_names_index_ = function_names_index_; return clone; @@ -322,7 +345,7 @@ bool V8::link(std::string_view debug_name) { fail(FailState::UnableToInitializeCode, std::string("Failed to load Wasm module due to a missing import: ") + std::string(module) + "." + std::string(name)); - break; + return false; } auto func = it->second.get()->callback_.get(); if (!equalValTypes(import_type->func()->params(), func->type()->params()) || @@ -334,7 +357,7 @@ bool V8::link(std::string_view debug_name) { printValTypes(import_type->func()->results()) + ", but host exports: " + printValTypes(func->type()->params()) + " -> " + printValTypes(func->type()->results())); - break; + return false; } imports.push_back(func); } break; @@ -344,12 +367,19 @@ bool V8::link(std::string_view debug_name) { fail(FailState::UnableToInitializeCode, "Failed to load Wasm module due to a missing import: " + std::string(module) + "." + std::string(name)); + return false; } break; case wasm::EXTERN_MEMORY: { assert(memory_ == nullptr); auto type = wasm::MemoryType::make(import_type->memory()->limits()); + if (type == nullptr) { + return false; + } memory_ = wasm::Memory::make(store_.get(), type.get()); + if (memory_ == nullptr) { + return false; + } imports.push_back(memory_.get()); } break; @@ -358,7 +388,13 @@ bool V8::link(std::string_view debug_name) { auto type = wasm::TableType::make(wasm::ValType::make(import_type->table()->element()->kind()), import_type->table()->limits()); + if (type == nullptr) { + return false; + } table_ = wasm::Table::make(store_.get(), type.get()); + if (table_ == nullptr) { + return false; + } imports.push_back(table_.get()); } break; } @@ -369,6 +405,10 @@ bool V8::link(std::string_view debug_name) { } instance_ = wasm::Instance::make(store_.get(), module_.get(), imports.data()); + if (instance_ == nullptr) { + fail(FailState::UnableToInitializeCode, "Failed to create new Wasm instance"); + return false; + } const auto export_types = module_.get()->exports(); const auto exports = instance_.get()->exports(); @@ -395,6 +435,9 @@ bool V8::link(std::string_view debug_name) { assert(export_item->memory() != nullptr); assert(memory_ == nullptr); memory_ = exports[i]->memory()->copy(); + if (memory_ == nullptr) { + return false; + } } break; case wasm::EXTERN_TABLE: { @@ -403,7 +446,7 @@ bool V8::link(std::string_view debug_name) { } } - return !isFailed(); + return true; } uint64_t V8::getMemorySize() { return memory_->data_size(); } diff --git a/src/wamr/wamr.cc b/src/wamr/wamr.cc index de9cee494..513978aa3 100644 --- a/src/wamr/wamr.cc +++ b/src/wamr/wamr.cc @@ -116,12 +116,19 @@ class Wamr : public WasmVm { bool Wamr::load(std::string_view bytecode, std::string_view, const std::unordered_map) { store_ = wasm_store_new(engine()); + if (store_ == nullptr) { + return false; + } WasmByteVec vec; wasm_byte_vec_new(vec.get(), bytecode.size(), bytecode.data()); + module_ = wasm_module_new(store_.get(), vec.get()); + if (module_ == nullptr) { + return false; + } - return module_ != nullptr; + return true; } static bool equalValTypes(const wasm_valtype_vec_t *left, const wasm_valtype_vec_t *right) { @@ -225,7 +232,7 @@ bool Wamr::link(std::string_view debug_name) { fail(FailState::UnableToInitializeCode, std::string("Failed to load Wasm module due to a missing import: ") + std::string(module_name) + "." + std::string(name)); - break; + return false; } auto func = it->second->callback_.get(); @@ -242,7 +249,7 @@ bool Wamr::link(std::string_view debug_name) { printValTypes(wasm_functype_results(exp_type)) + ", but host exports: " + printValTypes(wasm_functype_params(actual_type.get())) + " -> " + printValTypes(wasm_functype_results(actual_type.get()))); - break; + return false; } imports.push_back(wasm_func_as_extern(func)); } break; @@ -251,19 +258,32 @@ bool Wamr::link(std::string_view debug_name) { fail(FailState::UnableToInitializeCode, "Failed to load Wasm module due to a missing import: " + std::string(module_name) + "." + std::string(name)); + return false; } break; case WASM_EXTERN_MEMORY: { assert(memory_ == nullptr); const wasm_memorytype_t *memory_type = wasm_externtype_as_memorytype_const(extern_type); // owned by `extern_type` + if (memory_type == nullptr) { + return false; + } memory_ = wasm_memory_new(store_.get(), memory_type); + if (memory_ == nullptr) { + return false; + } imports.push_back(wasm_memory_as_extern(memory_.get())); } break; case WASM_EXTERN_TABLE: { assert(table_ == nullptr); const wasm_tabletype_t *table_type = wasm_externtype_as_tabletype_const(extern_type); // owned by `extern_type` + if (table_type == nullptr) { + return false; + } table_ = wasm_table_new(store_.get(), table_type, nullptr); + if (table_ == nullptr) { + return false; + } imports.push_back(wasm_table_as_extern(table_.get())); } break; } @@ -275,7 +295,10 @@ bool Wamr::link(std::string_view debug_name) { wasm_extern_vec_t imports_vec = {imports.size(), imports.data()}; instance_ = wasm_instance_new(store_.get(), module_.get(), &imports_vec, nullptr); - assert(instance_ != nullptr); + if (instance_ == nullptr) { + fail(FailState::UnableToInitializeCode, "Failed to create new Wasm instance"); + return false; + } WasmExportTypeVec export_types; wasm_module_exports(module_.get(), export_types.get()); @@ -302,7 +325,9 @@ bool Wamr::link(std::string_view debug_name) { case WASM_EXTERN_MEMORY: { assert(memory_ == nullptr); memory_ = wasm_memory_copy(wasm_extern_as_memory(actual_extern)); - assert(memory_ != nullptr); + if (memory_ == nullptr) { + return false; + } } break; case WASM_EXTERN_TABLE: { // TODO(mathetake): add support when/if needed. diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index ef47f7d39..7a9976735 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -112,28 +112,49 @@ class Wasmtime : public WasmVm { bool Wasmtime::load(std::string_view bytecode, std::string_view, const std::unordered_map) { store_ = wasm_store_new(engine()); + if (store_ == nullptr) { + return false; + } WasmByteVec vec; wasm_byte_vec_new(vec.get(), bytecode.size(), bytecode.data()); module_ = wasm_module_new(store_.get(), vec.get()); - if (!module_) { + if (module_ == nullptr) { return false; } shared_module_ = wasm_module_share(module_.get()); - assert(shared_module_ != nullptr); + if (shared_module_ == nullptr) { + return false; + } return true; } std::unique_ptr Wasmtime::clone() { assert(shared_module_ != nullptr); + auto clone = std::make_unique(); + if (clone == nullptr) { + return nullptr; + } - clone->integration().reset(integration()->clone()); clone->store_ = wasm_store_new(engine()); + if (clone->store_ == nullptr) { + return nullptr; + } + clone->module_ = wasm_module_obtain(clone->store_.get(), shared_module_.get()); + if (clone->module_ == nullptr) { + return nullptr; + } + + auto integration_clone = integration()->clone(); + if (integration_clone == nullptr) { + return nullptr; + } + clone->integration().reset(integration_clone); return clone; } @@ -239,7 +260,7 @@ bool Wasmtime::link(std::string_view debug_name) { fail(FailState::UnableToInitializeCode, std::string("Failed to load Wasm module due to a missing import: ") + std::string(module_name) + "." + std::string(name)); - break; + return false; } auto func = it->second->callback_.get(); @@ -256,7 +277,7 @@ bool Wasmtime::link(std::string_view debug_name) { printValTypes(wasm_functype_results(exp_type)) + ", but host exports: " + printValTypes(wasm_functype_params(actual_type.get())) + " -> " + printValTypes(wasm_functype_results(actual_type.get()))); - break; + return false; } imports.push_back(wasm_func_as_extern(func)); } break; @@ -265,19 +286,32 @@ bool Wasmtime::link(std::string_view debug_name) { fail(FailState::UnableToInitializeCode, "Failed to load Wasm module due to a missing import: " + std::string(module_name) + "." + std::string(name)); + return false; } break; case WASM_EXTERN_MEMORY: { assert(memory_ == nullptr); const wasm_memorytype_t *memory_type = wasm_externtype_as_memorytype_const(extern_type); // owned by `extern_type` + if (memory_type == nullptr) { + return false; + } memory_ = wasm_memory_new(store_.get(), memory_type); + if (memory_ == nullptr) { + return false; + } imports.push_back(wasm_memory_as_extern(memory_.get())); } break; case WASM_EXTERN_TABLE: { assert(table_ == nullptr); const wasm_tabletype_t *table_type = wasm_externtype_as_tabletype_const(extern_type); // owned by `extern_type` + if (table_type == nullptr) { + return false; + } table_ = wasm_table_new(store_.get(), table_type, nullptr); + if (table_ == nullptr) { + return false; + } imports.push_back(wasm_table_as_extern(table_.get())); } break; } @@ -289,7 +323,10 @@ bool Wasmtime::link(std::string_view debug_name) { wasm_extern_vec_t imports_vec = {imports.size(), imports.data()}; instance_ = wasm_instance_new(store_.get(), module_.get(), &imports_vec, nullptr); - assert(instance_ != nullptr); + if (instance_ == nullptr) { + fail(FailState::UnableToInitializeCode, "Failed to create new Wasm instance"); + return false; + } WasmExportTypeVec export_types; wasm_module_exports(module_.get(), export_types.get()); @@ -316,7 +353,9 @@ bool Wasmtime::link(std::string_view debug_name) { case WASM_EXTERN_MEMORY: { assert(memory_ == nullptr); memory_ = wasm_memory_copy(wasm_extern_as_memory(actual_extern)); - assert(memory_ != nullptr); + if (memory_ == nullptr) { + return false; + } } break; case WASM_EXTERN_TABLE: { // TODO(mathetake): add support when/if needed. diff --git a/src/wavm/wavm.cc b/src/wavm/wavm.cc index e7bbcf040..db39830b5 100644 --- a/src/wavm/wavm.cc +++ b/src/wavm/wavm.cc @@ -258,26 +258,51 @@ Wavm::~Wavm() { std::unique_ptr Wavm::clone() { auto wavm = std::make_unique(); - wavm->integration().reset(integration()->clone()); + if (wavm == nullptr) { + return nullptr; + } wavm->compartment_ = WAVM::Runtime::cloneCompartment(compartment_); + if (wavm->compartment_ == nullptr) { + return nullptr; + } + + wavm->context_ = WAVM::Runtime::cloneContext(context_, wavm->compartment_); + if (wavm->context_ == nullptr) { + return nullptr; + } + wavm->memory_ = WAVM::Runtime::remapToClonedCompartment(memory_, wavm->compartment_); wavm->memory_base_ = WAVM::Runtime::getMemoryBaseAddress(wavm->memory_); - wavm->context_ = WAVM::Runtime::createContext(wavm->compartment_); + wavm->module_instance_ = + WAVM::Runtime::remapToClonedCompartment(module_instance_, wavm->compartment_); for (auto &p : intrinsic_module_instances_) { wavm->intrinsic_module_instances_.emplace( p.first, WAVM::Runtime::remapToClonedCompartment(p.second, wavm->compartment_)); } - wavm->module_instance_ = - WAVM::Runtime::remapToClonedCompartment(module_instance_, wavm->compartment_); + + auto integration_clone = integration()->clone(); + if (integration_clone == nullptr) { + return nullptr; + } + wavm->integration().reset(integration_clone); + return wavm; } bool Wavm::load(std::string_view bytecode, std::string_view precompiled, const std::unordered_map) { compartment_ = WAVM::Runtime::createCompartment(); + if (compartment_ == nullptr) { + return false; + } + context_ = WAVM::Runtime::createContext(compartment_); + if (context_ == nullptr) { + return false; + } + if (!WASM::loadBinaryModule(reinterpret_cast(bytecode.data()), bytecode.size(), ir_module_)) { return false; @@ -286,11 +311,18 @@ bool Wavm::load(std::string_view bytecode, std::string_view precompiled, if (!precompiled.empty()) { module_ = WAVM::Runtime::loadPrecompiledModule( ir_module_, {precompiled.data(), precompiled.data() + precompiled.size()}); + if (module_ == nullptr) { + return false; + } + } else { module_ = WAVM::Runtime::compileModule(ir_module_); + if (module_ == nullptr) { + return false; + } } - return module_ != nullptr; + return true; } bool Wavm::link(std::string_view debug_name) { @@ -298,9 +330,13 @@ bool Wavm::link(std::string_view debug_name) { for (auto &p : intrinsic_modules_) { auto instance = Intrinsics::instantiateModule(compartment_, {&intrinsic_modules_[p.first]}, std::string(p.first)); + if (instance == nullptr) { + return false; + } intrinsic_module_instances_.emplace(p.first, instance); rootResolver.moduleNameToInstanceMap().set(p.first, instance); } + WAVM::Runtime::LinkResult link_result = linkModule(ir_module_, rootResolver); if (!link_result.missingImports.empty()) { for (auto &i : link_result.missingImports) { @@ -309,10 +345,20 @@ bool Wavm::link(std::string_view debug_name) { fail(FailState::MissingFunction, "Failed to load Wasm module due to a missing import(s)"); return false; } + module_instance_ = instantiateModule( compartment_, module_, std::move(link_result.resolvedImports), std::string(debug_name)); + if (module_instance_ == nullptr) { + return false; + } + memory_ = getDefaultMemory(module_instance_); + if (memory_ == nullptr) { + return false; + } + memory_base_ = WAVM::Runtime::getMemoryBaseAddress(memory_); + return true; } diff --git a/test/runtime_test.cc b/test/runtime_test.cc index d741919a9..10ca29bd6 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -91,6 +91,40 @@ TEST_P(TestVM, Clone) { ASSERT_NE(100, word.u64_); } +#if defined(__linux__) && defined(__x86_64__) + +TEST_P(TestVM, CloneUntilOutOfMemory) { + if (vm_->cloneable() == proxy_wasm::Cloneable::NotCloneable) { + return; + } + if (runtime_ == "wavm") { + // TODO(PiotrSikora): Figure out why this fails on the CI. + return; + } + + auto source = readTestWasmFile("abi_export.wasm"); + ASSERT_TRUE(vm_->load(source, {}, {})); + ASSERT_TRUE(vm_->link("")); + + std::vector> clones; + for (;;) { + auto clone = vm_->clone(); + if (clone == nullptr) { + break; + } + if (clone->cloneable() != proxy_wasm::Cloneable::InstantiatedModule) { + if (clone->link("") == false) { + break; + } + } + // Prevent clone from droping out of scope and freeing memory. + clones.push_back(std::move(clone)); + } + EXPECT_GE(clones.size(), 1000); +} + +#endif + class TestContext : public ContextBase { public: TestContext(){};