Repository: tetratelabs/wazero
Pull request #1281 (2023-03-24 07:09:54)

Import function type check at validation phrase


Patch:
diff --git a/internal/wasm/module.go b/internal/wasm/module.go
index f3a265ca..c55e93b0 100644
--- a/internal/wasm/module.go
+++ b/internal/wasm/module.go
@@ -455,6 +455,10 @@ func (m *Module) validateImports(enabledFeatures api.CoreFeatures) error {
 			return fmt.Errorf("import[%d] has an empty module name", i)
 		}
 		switch imp.Type {
+		case ExternTypeFunc:
+			if int(imp.DescFunc) >= len(m.TypeSection) {
+				return fmt.Errorf("invalid import[%q.%q] function: type index out of range", imp.Module, imp.Name)
+			}
 		case ExternTypeGlobal:
 			if !imp.DescGlobal.Mutable {
 				continue
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index 61b7ead6..59e04c56 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -407,19 +407,12 @@ func (m *ModuleInstance) resolveImports(module *Module, importedModules map[stri
 
 		switch i.Type {
 		case ExternTypeFunc:
-			typeIndex := i.DescFunc
-			// TODO: this shouldn't be possible as invalid should fail validate
-			if int(typeIndex) >= len(module.TypeSection) {
-				err = errorInvalidImport(i, idx, fmt.Errorf("function type out of range"))
-				return
-			}
-			expectedType := &module.TypeSection[i.DescFunc]
 			importedFunction := &importedModule.Functions[imported.Index]
-
-			d := importedFunction.Definition
-			if !expectedType.EqualsSignature(d.ParamTypes(), d.ResultTypes()) {
-				actualType := &FunctionType{Params: d.ParamTypes(), Results: d.ResultTypes()}
-				err = errorInvalidImport(i, idx, fmt.Errorf("signature mismatch: %s != %s", expectedType, actualType))
+			expectedTypeID := m.TypeIDs[i.DescFunc]
+			importedTypeID := importedFunction.TypeID
+			if importedTypeID != expectedTypeID {
+				err = errorInvalidImport(i, idx, fmt.Errorf("signature mismatch: %s != %s",
+					&module.TypeSection[i.DescFunc], importedFunction.Type))
 				return
 			}
 			m.Functions[fs] = *importedFunction
@@ -430,6 +423,7 @@ func (m *ModuleInstance) resolveImports(module *Module, importedModules map[stri
 			if expected.Type != importedTable.Type {
 				err = errorInvalidImport(i, idx, fmt.Errorf("table type mismatch: %s != %s",
 					RefTypeName(expected.Type), RefTypeName(importedTable.Type)))
+				return
 			}
 
 			if expected.Min > importedTable.Min {


Test patch:
diff --git a/internal/wasm/module_test.go b/internal/wasm/module_test.go
index 8bf192f0..380aba88 100644
--- a/internal/wasm/module_test.go
+++ b/internal/wasm/module_test.go
@@ -585,6 +585,12 @@ func TestModule_validateImports(t *testing.T) {
 			enabledFeatures: api.CoreFeaturesV1,
 			i:               &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
 		},
+		{
+			name:            "func type index out of range ",
+			enabledFeatures: api.CoreFeaturesV1,
+			i:               &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 100},
+			expectedErr:     "invalid import[\"m\".\"n\"] function: type index out of range",
+		},
 		{
 			name:            "global var disabled",
 			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
@@ -621,7 +627,7 @@ func TestModule_validateImports(t *testing.T) {
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
-			m := Module{}
+			m := Module{TypeSection: []FunctionType{{}}}
 			if tc.i != nil {
 				m.ImportSection = []Import{*tc.i}
 			}
diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go
index 54957c65..a6640b5d 100644
--- a/internal/wasm/store_test.go
+++ b/internal/wasm/store_test.go
@@ -658,11 +658,12 @@ func Test_resolveImports(t *testing.T) {
 		require.EqualError(t, err, "\"unknown\" is not exported in module \"test\"")
 	})
 	t.Run("func", func(t *testing.T) {
+		typeIDs := []FunctionTypeID{100, 200}
 		t.Run("ok", func(t *testing.T) {
 			externMod := &ModuleInstance{
 				Functions: []FunctionInstance{
-					{Definition: &FunctionDefinition{funcType: &FunctionType{Results: []ValueType{ValueTypeF32}}}},
-					{Definition: &FunctionDefinition{funcType: &FunctionType{Results: []ValueType{ValueTypeI32}}}},
+					{TypeID: typeIDs[0], Definition: &FunctionDefinition{funcType: &FunctionType{Results: []ValueType{ValueTypeF32}}}},
+					{TypeID: typeIDs[1], Definition: &FunctionDefinition{funcType: &FunctionType{Results: []ValueType{ValueTypeI32}}}},
 				},
 				Exports: map[string]*Export{
 					name: {Type: ExternTypeFunc, Index: 0},
@@ -681,24 +682,16 @@ func Test_resolveImports(t *testing.T) {
 				},
 			}
 
-			m := &ModuleInstance{Functions: make([]FunctionInstance, 2)}
+			m := &ModuleInstance{Functions: make([]FunctionInstance, 2), TypeIDs: typeIDs}
 			err := m.resolveImports(module, importedModules)
 			require.NoError(t, err)
 
 			require.Equal(t, m.Functions[0], externMod.Functions[0])
 			require.Equal(t, m.Functions[1], externMod.Functions[1])
 		})
-		t.Run("type out of range", func(t *testing.T) {
-			modules := map[string]*ModuleInstance{
-				moduleName: {Exports: map[string]*Export{name: {}}, Name: moduleName},
-			}
-			m := &ModuleInstance{Functions: make([]FunctionInstance, 1)}
-			err := m.resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 100}}}, modules)
-			require.EqualError(t, err, "import[0] func[test.target]: function type out of range")
-		})
 		t.Run("signature mismatch", func(t *testing.T) {
 			externMod := &ModuleInstance{
-				Functions: []FunctionInstance{{Definition: &FunctionDefinition{funcType: &FunctionType{}}}},
+				Functions: []FunctionInstance{{TypeID: 123435, Type: &FunctionType{}}},
 				Exports: map[string]*Export{
 					name: {Type: ExternTypeFunc, Index: 0},
 				},
@@ -709,7 +702,7 @@ func Test_resolveImports(t *testing.T) {
 				ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeFunc, DescFunc: 0}},
 			}
 
-			m := &ModuleInstance{Functions: make([]FunctionInstance, 1)}
+			m := &ModuleInstance{Functions: make([]FunctionInstance, 1), TypeIDs: typeIDs}
 			err := m.resolveImports(module, map[string]*ModuleInstance{moduleName: externMod})
 			require.EqualError(t, err, "import[0] func[test.target]: signature mismatch: v_f32 != v_v")
 		})
diff --git a/internal/wasm/table_test.go b/internal/wasm/table_test.go
index 68bf26b4..7e921474 100644
--- a/internal/wasm/table_test.go
+++ b/internal/wasm/table_test.go
@@ -61,6 +61,20 @@ func Test_resolveImports_table(t *testing.T) {
 		err := m.resolveImports(&Module{ImportSection: []Import{{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}}}, importedModules)
 		require.EqualError(t, err, "import[0] table[test.target]: maximum size mismatch: 10, but actual has no max")
 	})
+	t.Run("type mismatch", func(t *testing.T) {
+		importedModules := map[string]*ModuleInstance{
+			moduleName: {
+				Tables:  []*TableInstance{{Type: RefTypeFuncref}},
+				Exports: map[string]*Export{name: {Type: ExternTypeTable}},
+				Name:    moduleName,
+			},
+		}
+		m := &ModuleInstance{Tables: make([]*TableInstance, 1)}
+		err := m.resolveImports(&Module{ImportSection: []Import{
+			{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: Table{Type: RefTypeExternref}},
+		}}, importedModules)
+		require.EqualError(t, err, "import[0] table[test.target]: table type mismatch: externref != funcref")
+	})
 }
 
 var codeEnd = Code{Body: []byte{OpcodeEnd}}