Repository: tetratelabs/wazero
Pull request #703 (2022-07-15 04:17:22)

validation: not modify loop block type on unreachable br_table
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>


Patch:
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index 390073d6..97823bf7 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -465,15 +465,16 @@ func (m *Module) validateFunctionWithMaxStackValues(
 			}
 			lnLabel := controlBlockStack[len(controlBlockStack)-1-int(ln)]
 			var defaultLabelType []ValueType
+			// Below, we might modify the slice in case of unreachable. Therefore,
+			// we have to copy the content of block result types, otherwise the original
+			// function type might result in invalid value types if the block is the outermost label
+			// which equals the function's type.
 			if lnLabel.op != OpcodeLoop { // Loop operation doesn't require results since the continuation is the beginning of the loop.
 				defaultLabelType = make([]ValueType, len(lnLabel.blockType.Results))
-				// Below, we might modify the slice in case of unreachable. Therefore,
-				// we have to copy the content of block result types, otherwise the original
-				// function type might result in invalid value types if the block is the outermost label
-				// which equals the function's type.
 				copy(defaultLabelType, lnLabel.blockType.Results)
 			} else {
-				defaultLabelType = lnLabel.blockType.Params
+				defaultLabelType = make([]ValueType, len(lnLabel.blockType.Params))
+				copy(defaultLabelType, lnLabel.blockType.Params)
 			}
 
 			if enabledFeatures.Get(FeatureReferenceTypes) {


Test patch:
diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go
index d2c472e4..108a22fe 100644
--- a/internal/wasm/func_validation_test.go
+++ b/internal/wasm/func_validation_test.go
@@ -3353,33 +3353,66 @@ func TestDecodeBlockType(t *testing.T) {
 // original function type during the function validation with the presence of unreachable br_table
 // targeting the function return label.
 func TestFuncValidation_UnreachableBrTable_NotModifyTypes(t *testing.T) {
-	funcType := &FunctionType{Params: []ValueType{},
-		// Meaning that the signature of outermost label has non-trivial results.
-		Results: []ValueType{i32, i64},
-	}
-	m := &Module{
-		TypeSection:     []*FunctionType{funcType},
-		FunctionSection: []Index{0},
-		CodeSection: []*Code{
-			{Body: []byte{
-				OpcodeUnreachable,
-				// Having br_table in unreachable state.
-				OpcodeI32Const, 1,
-				// Setting the destination as lalbels of index 0 which
-				// is the function return. Therefore, the expected value types
-				// for each label can be "any".
-				OpcodeBrTable, 2, 0, 0, 0,
-				OpcodeEnd,
-			}},
+	funcType := &FunctionType{Results: []ValueType{i32, i64}, Params: []ValueType{i32}}
+	copiedFuncType := &FunctionType{Params: make([]ValueType, len(funcType.Params)),
+		Results: make([]ValueType, len(funcType.Results))}
+
+	copy(copiedFuncType.Results, funcType.Results)
+	copy(copiedFuncType.Params, funcType.Params)
+
+	for _, tc := range []struct {
+		name string
+		m    *Module
+	}{
+		{
+			name: "on function return",
+			m: &Module{
+				TypeSection:     []*FunctionType{funcType},
+				FunctionSection: []Index{0},
+				CodeSection: []*Code{
+					{Body: []byte{
+						OpcodeUnreachable,
+						// Having br_table in unreachable state.
+						OpcodeI32Const, 1,
+						// Setting the destination as labels of index 0 which
+						// is the function return.
+						OpcodeBrTable, 2, 0, 0, 0,
+						OpcodeEnd,
+					}},
+				},
+			},
 		},
-	}
-	err := m.validateFunction(FeatureReferenceTypes, 0, nil, nil, nil, nil, nil)
-	require.NoError(t, err)
+		{
+			name: "on loop return",
+			m: &Module{
+				TypeSection:     []*FunctionType{funcType},
+				FunctionSection: []Index{0},
+				CodeSection: []*Code{
+					{Body: []byte{
+						OpcodeUnreachable,
+						OpcodeLoop, 0, // indicates that loop has funcType as its block type
+						OpcodeUnreachable,
+						// Having br_table in unreachable state.
+						OpcodeI32Const, 1,
+						// Setting the destination as labels of index 0 which
+						// is the loop return.
+						OpcodeBrTable, 2, 0, 0, 0,
+						OpcodeEnd, // End of loop
+						OpcodeEnd,
+					}},
+				},
+			},
+		},
+	} {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			err := tc.m.validateFunction(Features20220419, 0, nil, nil, nil, nil, nil)
+			require.NoError(t, err)
 
-	afterValidationResultTypes := funcType.Results
-	require.Equal(t, 2, len(afterValidationResultTypes))
-	require.Equal(t, i32, afterValidationResultTypes[0])
-	require.Equal(t, i64, afterValidationResultTypes[1])
+			// Ensures that funcType has remained intact.
+			require.Equal(t, copiedFuncType, funcType)
+		})
+	}
 }
 
 func TestModule_funcValidation_loopWithParams(t *testing.T) {