[wasm-gc] Check for subtyping when importing function
When importing a WasmExportedFunction into a module, we checked that its type is equivalent with the declared type of the import. Instead, we should check that the imported function has an isorecursive subtype of the declared type. Change-Id: I2a5f68d4c4c8c65a0eed5b82e8e825affb832cfe Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4061732 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#84593}
This commit is contained in:
parent
04e6519dd0
commit
86beeb9910
@ -7906,12 +7906,12 @@ bool ResolveBoundJSFastApiFunction(const wasm::FunctionSig* expected_sig,
|
||||
|
||||
WasmImportData ResolveWasmImportCall(
|
||||
Handle<JSReceiver> callable, const wasm::FunctionSig* expected_sig,
|
||||
const wasm::WasmModule* module,
|
||||
uint32_t expected_canonical_type_index, const wasm::WasmModule* module,
|
||||
const wasm::WasmFeatures& enabled_features) {
|
||||
Isolate* isolate = callable->GetIsolate();
|
||||
if (WasmExportedFunction::IsWasmExportedFunction(*callable)) {
|
||||
auto imported_function = Handle<WasmExportedFunction>::cast(callable);
|
||||
if (!imported_function->MatchesSignature(module, expected_sig)) {
|
||||
if (!imported_function->MatchesSignature(expected_canonical_type_index)) {
|
||||
return {WasmImportCallKind::kLinkError, callable, wasm::kNoSuspend};
|
||||
}
|
||||
uint32_t func_index =
|
||||
|
@ -120,7 +120,8 @@ struct WasmImportData {
|
||||
// is why the ultimate target is returned as well.
|
||||
V8_EXPORT_PRIVATE WasmImportData ResolveWasmImportCall(
|
||||
Handle<JSReceiver> callable, const wasm::FunctionSig* sig,
|
||||
const wasm::WasmModule* module, const wasm::WasmFeatures& enabled_features);
|
||||
uint32_t expected_canonical_type_index, const wasm::WasmModule* module,
|
||||
const wasm::WasmFeatures& enabled_features);
|
||||
|
||||
// Compiles an import call wrapper, which allows Wasm to call imports.
|
||||
V8_EXPORT_PRIVATE wasm::WasmCompilationResult CompileWasmImportCallWrapper(
|
||||
|
@ -1676,8 +1676,8 @@ Handle<WasmResumeData> Factory::NewWasmResumeData(
|
||||
Handle<WasmExportedFunctionData> Factory::NewWasmExportedFunctionData(
|
||||
Handle<CodeT> export_wrapper, Handle<WasmInstanceObject> instance,
|
||||
Address call_target, Handle<Object> ref, int func_index,
|
||||
const wasm::FunctionSig* sig, int wrapper_budget, Handle<Map> rtt,
|
||||
wasm::Promise promise) {
|
||||
const wasm::FunctionSig* sig, uint32_t canonical_type_index,
|
||||
int wrapper_budget, Handle<Map> rtt, wasm::Promise promise) {
|
||||
Handle<WasmInternalFunction> internal =
|
||||
NewWasmInternalFunction(call_target, Handle<HeapObject>::cast(ref), rtt);
|
||||
Map map = *wasm_exported_function_data_map();
|
||||
@ -1691,6 +1691,7 @@ Handle<WasmExportedFunctionData> Factory::NewWasmExportedFunctionData(
|
||||
result.set_instance(*instance);
|
||||
result.set_function_index(func_index);
|
||||
result.init_sig(isolate(), sig);
|
||||
result.set_canonical_type_index(canonical_type_index);
|
||||
result.set_wrapper_budget(wrapper_budget);
|
||||
// We can't skip the write barrier when V8_EXTERNAL_CODE_SPACE is enabled
|
||||
// because in this case the CodeT (CodeDataContainer) objects are not
|
||||
|
@ -636,8 +636,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
|
||||
Handle<WasmExportedFunctionData> NewWasmExportedFunctionData(
|
||||
Handle<CodeT> export_wrapper, Handle<WasmInstanceObject> instance,
|
||||
Address call_target, Handle<Object> ref, int func_index,
|
||||
const wasm::FunctionSig* sig, int wrapper_budget, Handle<Map> rtt,
|
||||
wasm::Promise promise);
|
||||
const wasm::FunctionSig* sig, uint32_t canonical_type_index,
|
||||
int wrapper_budget, Handle<Map> rtt, wasm::Promise promise);
|
||||
Handle<WasmApiFunctionRef> NewWasmApiFunctionRef(
|
||||
Handle<JSReceiver> callable, wasm::Suspend suspend,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
@ -100,23 +100,28 @@ ValueType TypeCanonicalizer::CanonicalizeValueType(
|
||||
module->isorecursive_canonical_type_ids[type.ref_index()]);
|
||||
}
|
||||
|
||||
bool TypeCanonicalizer::IsCanonicalSubtype(uint32_t sub_index,
|
||||
uint32_t super_index,
|
||||
const WasmModule* sub_module,
|
||||
const WasmModule* super_module) {
|
||||
bool TypeCanonicalizer::IsCanonicalSubtype(uint32_t canonical_sub_index,
|
||||
uint32_t canonical_super_index) {
|
||||
// Multiple threads could try to register and access recursive groups
|
||||
// concurrently.
|
||||
// TODO(manoskouk): Investigate if we can improve this synchronization.
|
||||
base::MutexGuard mutex_guard(&mutex_);
|
||||
while (canonical_sub_index != kNoSuperType) {
|
||||
if (canonical_sub_index == canonical_super_index) return true;
|
||||
canonical_sub_index = canonical_supertypes_[canonical_sub_index];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeCanonicalizer::IsCanonicalSubtype(uint32_t sub_index,
|
||||
uint32_t super_index,
|
||||
const WasmModule* sub_module,
|
||||
const WasmModule* super_module) {
|
||||
uint32_t canonical_super =
|
||||
super_module->isorecursive_canonical_type_ids[super_index];
|
||||
uint32_t canonical_sub =
|
||||
sub_module->isorecursive_canonical_type_ids[sub_index];
|
||||
while (canonical_sub != kNoSuperType) {
|
||||
if (canonical_sub == canonical_super) return true;
|
||||
canonical_sub = canonical_supertypes_[canonical_sub];
|
||||
}
|
||||
return false;
|
||||
return IsCanonicalSubtype(canonical_sub, canonical_super);
|
||||
}
|
||||
|
||||
TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef(
|
||||
|
@ -52,6 +52,11 @@ class TypeCanonicalizer {
|
||||
// signature.
|
||||
V8_EXPORT_PRIVATE uint32_t AddRecursiveGroup(const FunctionSig* sig);
|
||||
|
||||
// Returns if {canonical_sub_index} is a canonical subtype of
|
||||
// {canonical_super_index}.
|
||||
V8_EXPORT_PRIVATE bool IsCanonicalSubtype(uint32_t canonical_sub_index,
|
||||
uint32_t canonical_super_index);
|
||||
|
||||
// Returns if the type at {sub_index} in {sub_module} is a subtype of the
|
||||
// type at {super_index} in {super_module} after canonicalization.
|
||||
V8_EXPORT_PRIVATE bool IsCanonicalSubtype(uint32_t sub_index,
|
||||
|
@ -1125,8 +1125,11 @@ bool InstanceBuilder::ProcessImportedFunction(
|
||||
}
|
||||
auto js_receiver = Handle<JSReceiver>::cast(value);
|
||||
const FunctionSig* expected_sig = module_->functions[func_index].sig;
|
||||
auto resolved = compiler::ResolveWasmImportCall(js_receiver, expected_sig,
|
||||
module_, enabled_);
|
||||
uint32_t sig_index = module_->functions[func_index].sig_index;
|
||||
uint32_t canonical_type_index =
|
||||
module_->isorecursive_canonical_type_ids[sig_index];
|
||||
auto resolved = compiler::ResolveWasmImportCall(
|
||||
js_receiver, expected_sig, canonical_type_index, module_, enabled_);
|
||||
compiler::WasmImportCallKind kind = resolved.kind;
|
||||
js_receiver = resolved.callable;
|
||||
switch (kind) {
|
||||
@ -1589,8 +1592,11 @@ void InstanceBuilder::CompileImportWrappers(
|
||||
auto js_receiver = Handle<JSReceiver>::cast(value);
|
||||
uint32_t func_index = module_->import_table[index].index;
|
||||
const FunctionSig* sig = module_->functions[func_index].sig;
|
||||
auto resolved =
|
||||
compiler::ResolveWasmImportCall(js_receiver, sig, module_, enabled_);
|
||||
uint32_t sig_index = module_->functions[func_index].sig_index;
|
||||
uint32_t canonical_type_index =
|
||||
module_->isorecursive_canonical_type_ids[sig_index];
|
||||
auto resolved = compiler::ResolveWasmImportCall(
|
||||
js_receiver, sig, canonical_type_index, module_, enabled_);
|
||||
compiler::WasmImportCallKind kind = resolved.kind;
|
||||
if (kind == compiler::WasmImportCallKind::kWasmToWasm ||
|
||||
kind == compiler::WasmImportCallKind::kLinkError ||
|
||||
@ -1607,9 +1613,6 @@ void InstanceBuilder::CompileImportWrappers(
|
||||
expected_arity =
|
||||
shared.internal_formal_parameter_count_without_receiver();
|
||||
}
|
||||
uint32_t canonical_type_index =
|
||||
module_->isorecursive_canonical_type_ids[module_->functions[func_index]
|
||||
.sig_index];
|
||||
WasmImportWrapperCache::CacheKey key(kind, canonical_type_index,
|
||||
expected_arity, resolved.suspend);
|
||||
if (cache_scope[key] != nullptr) {
|
||||
|
@ -1425,7 +1425,7 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
|
||||
// and permissions switching.
|
||||
const wasm::WasmFeatures enabled = native_module->enabled_features();
|
||||
auto resolved = compiler::ResolveWasmImportCall(
|
||||
callable, sig, instance->module(), enabled);
|
||||
callable, sig, canonical_sig_index, instance->module(), enabled);
|
||||
compiler::WasmImportCallKind kind = resolved.kind;
|
||||
callable = resolved.callable; // Update to ultimate target.
|
||||
DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind);
|
||||
@ -1937,10 +1937,13 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
|
||||
export_wrapper->builtin_id() == Builtin::kWasmReturnPromiseOnSuspend
|
||||
? wasm::kPromise
|
||||
: wasm::kNoPromise;
|
||||
uint32_t sig_index = instance->module()->functions[func_index].sig_index;
|
||||
uint32_t canonical_type_index =
|
||||
instance->module()->isorecursive_canonical_type_ids[sig_index];
|
||||
Handle<WasmExportedFunctionData> function_data =
|
||||
factory->NewWasmExportedFunctionData(
|
||||
export_wrapper, instance, call_target, ref, func_index, sig,
|
||||
wasm::kGenericWrapperBudget, rtt, promise);
|
||||
canonical_type_index, wasm::kGenericWrapperBudget, rtt, promise);
|
||||
|
||||
MaybeHandle<String> maybe_name;
|
||||
bool is_asm_js_module = instance->module_object().is_asm_js();
|
||||
@ -2000,20 +2003,10 @@ const wasm::FunctionSig* WasmExportedFunction::sig() {
|
||||
}
|
||||
|
||||
bool WasmExportedFunction::MatchesSignature(
|
||||
const WasmModule* other_module, const wasm::FunctionSig* other_sig) {
|
||||
const wasm::FunctionSig* sig = this->sig();
|
||||
if (sig->parameter_count() != other_sig->parameter_count() ||
|
||||
sig->return_count() != other_sig->return_count()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sig->all().size(); i++) {
|
||||
if (!wasm::EquivalentTypes(sig->all()[i], other_sig->all()[i],
|
||||
this->instance().module(), other_module)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
uint32_t other_canonical_type_index) {
|
||||
return wasm::GetWasmEngine()->type_canonicalizer()->IsCanonicalSubtype(
|
||||
this->shared().wasm_exported_function_data().canonical_type_index(),
|
||||
other_canonical_type_index);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -618,8 +618,7 @@ class WasmExportedFunction : public JSFunction {
|
||||
|
||||
V8_EXPORT_PRIVATE const wasm::FunctionSig* sig();
|
||||
|
||||
bool MatchesSignature(const wasm::WasmModule* other_module,
|
||||
const wasm::FunctionSig* other_sig);
|
||||
bool MatchesSignature(uint32_t other_canonical_sig_index);
|
||||
|
||||
// Return a null-terminated string with the debug name in the form
|
||||
// 'js-to-wasm:<sig>'.
|
||||
|
@ -71,6 +71,7 @@ extern class WasmExportedFunctionData extends WasmFunctionData {
|
||||
@if(V8_EXTERNAL_CODE_SPACE) c_wrapper_code: CodeDataContainer;
|
||||
@ifnot(V8_EXTERNAL_CODE_SPACE) c_wrapper_code: Code;
|
||||
packed_args_size: Smi;
|
||||
canonical_type_index: Smi;
|
||||
sig: ExternalPointer; // wasm::FunctionSig*
|
||||
}
|
||||
|
||||
|
@ -75,15 +75,15 @@ TestingModuleBuilder::TestingModuleBuilder(
|
||||
|
||||
if (maybe_import) {
|
||||
// Manually compile an import wrapper and insert it into the instance.
|
||||
uint32_t canonical_type_index =
|
||||
GetTypeCanonicalizer()->AddRecursiveGroup(maybe_import->sig);
|
||||
auto resolved = compiler::ResolveWasmImportCall(
|
||||
maybe_import->js_function, maybe_import->sig,
|
||||
maybe_import->js_function, maybe_import->sig, canonical_type_index,
|
||||
instance_object_->module(), enabled_features_);
|
||||
compiler::WasmImportCallKind kind = resolved.kind;
|
||||
Handle<JSReceiver> callable = resolved.callable;
|
||||
WasmImportWrapperCache::ModificationScope cache_scope(
|
||||
native_module_->import_wrapper_cache());
|
||||
uint32_t canonical_type_index =
|
||||
GetTypeCanonicalizer()->AddRecursiveGroup(maybe_import->sig);
|
||||
WasmImportWrapperCache::CacheKey key(
|
||||
kind, canonical_type_index,
|
||||
static_cast<int>(maybe_import->sig->parameter_count()), kNoSuspend);
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
(function Test1() {
|
||||
(function TestImportedRefCall() {
|
||||
print(arguments.callee.name);
|
||||
var exporting_instance = (function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
|
||||
@ -120,6 +121,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
})();
|
||||
|
||||
(function TestFromJSSlowPath() {
|
||||
print(arguments.callee.name);
|
||||
var builder = new WasmModuleBuilder();
|
||||
var sig_index = builder.addType(kSig_i_i);
|
||||
|
||||
@ -135,3 +137,38 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
// {undefined} is converted to 0.
|
||||
assertEquals(0, instance.exports.main(fun, 1000));
|
||||
})();
|
||||
|
||||
(function TestImportedFunctionSubtyping() {
|
||||
print(arguments.callee.name);
|
||||
var exporting_instance = (function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
let super_struct = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
let sub_struct = builder.addStruct(
|
||||
[makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct);
|
||||
let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)],
|
||||
[kWasmI32]))
|
||||
let sub_sig = builder.addType(makeSig([wasmRefNullType(super_struct)],
|
||||
[kWasmI32]), super_sig)
|
||||
|
||||
builder.addFunction("exported_function", sub_sig)
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, super_struct, 0])
|
||||
.exportFunc();
|
||||
|
||||
return builder.instantiate({});
|
||||
})();
|
||||
|
||||
var instance = (function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
// These should canonicalize to the same types as the exporting instance.
|
||||
let super_struct = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
let sub_struct = builder.addStruct(
|
||||
[makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct);
|
||||
let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)],
|
||||
[kWasmI32]))
|
||||
builder.addImport("m", "f", super_sig);
|
||||
|
||||
// Import is a function of the declared type.
|
||||
return builder.instantiate({m: {f:
|
||||
exporting_instance.exports.exported_function}});
|
||||
})();
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user