Updates Spectest to the latest (May 23, 2023) (#1490)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-05-23 15:09:36 +10:00
committed by GitHub
parent 50723a0fd2
commit 4aca6fbd0e
126 changed files with 755 additions and 1537 deletions

View File

@@ -33,6 +33,10 @@ func decodeElementInitValueVector(r *bytes.Reader) ([]wasm.Index, error) {
if err != nil {
return nil, fmt.Errorf("read function index: %w", err)
}
if u32 >= wasm.MaximumFunctionIndex {
return nil, fmt.Errorf("too large function index in Element init: %d", u32)
}
vec[i] = u32
}
return vec, nil
@@ -56,6 +60,9 @@ func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enable
return nil, fmt.Errorf("element type mismatch: want %s, but constexpr has funcref", wasm.RefTypeName(elemType))
}
v, _, _ := leb128.LoadUint32(expr.Data)
if v >= wasm.MaximumFunctionIndex {
return nil, fmt.Errorf("too large function index in Element init: %d", v)
}
vec[i] = v
case wasm.OpcodeRefNull:
if elemType != expr.Data[0] {
@@ -63,6 +70,14 @@ func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enable
wasm.RefTypeName(elemType), wasm.RefTypeName(expr.Data[0]))
}
vec[i] = wasm.ElementInitNullReference
case wasm.OpcodeGlobalGet:
i32, _, _ := leb128.LoadInt32(expr.Data)
if elemType != wasm.RefTypeFuncref {
return nil, fmt.Errorf("element type mismatch: want %s, but requires funcref", wasm.RefTypeName(elemType))
}
// Resolving the function index is done at instantiation phase. See the comment on
// wasm.ElementInitImportedGlobalFunctionReference.
vec[i] = wasm.ElementInitImportedGlobalFunctionReference | wasm.Index(i32)
default:
return nil, fmt.Errorf("const expr must be either ref.null or ref.func but was %s", wasm.InstructionName(expr.Opcode))
}

View File

@@ -17,8 +17,9 @@ func Test_ensureElementKindFuncRef(t *testing.T) {
func Test_decodeElementInitValueVector(t *testing.T) {
tests := []struct {
in []byte
exp []wasm.Index
in []byte
exp []wasm.Index
expErr string
}{
{
in: []byte{0},
@@ -28,14 +29,25 @@ func Test_decodeElementInitValueVector(t *testing.T) {
in: []byte{5, 1, 2, 3, 4, 5},
exp: []wasm.Index{1, 2, 3, 4, 5},
},
{
in: []byte{
1,
0xff, 0xff, 0xff, 0xff, 0xf,
},
expErr: "too large function index in Element init: 4294967295",
},
}
for i, tt := range tests {
tc := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
actual, err := decodeElementInitValueVector(bytes.NewReader(tc.in))
require.NoError(t, err)
require.Equal(t, tc.exp, actual)
if tc.expErr != "" {
require.EqualError(t, err, tc.expErr)
} else {
require.NoError(t, err)
require.Equal(t, tc.exp, actual)
}
})
}
}
@@ -65,14 +77,20 @@ func Test_decodeElementConstExprVector(t *testing.T) {
},
{
in: []byte{
3, // Three indexes.
4, // Three indexes.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeGlobalGet, 1, wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: []wasm.Index{wasm.ElementInitNullReference, 165675008, wasm.ElementInitNullReference},
exp: []wasm.Index{
wasm.ElementInitNullReference,
16256,
wasm.ElementInitImportedGlobalFunctionReference | 1,
wasm.ElementInitNullReference,
},
refType: wasm.RefTypeFuncref,
features: api.CoreFeatureBulkMemoryOperations,
},
@@ -143,6 +161,20 @@ func Test_decodeElementConstExprVector_errors(t *testing.T) {
features: api.CoreFeaturesV2,
expErr: "element type mismatch: want externref, but constexpr has funcref",
},
{
name: "too large index - ref.fuc",
in: []byte{1, wasm.OpcodeRefFunc, 0xff, 0xff, 0xff, 0xff, 0xf, wasm.OpcodeEnd},
refType: wasm.RefTypeFuncref,
features: api.CoreFeaturesV2,
expErr: "too large function index in Element init: 4294967295",
},
{
name: "type mismatch - global.get",
in: []byte{1, wasm.OpcodeGlobalGet, 0, wasm.OpcodeEnd},
refType: wasm.RefTypeExternref,
features: api.CoreFeaturesV2,
expErr: "element type mismatch: want externref, but requires funcref",
},
}
for _, tt := range tests {
@@ -289,13 +321,13 @@ func TestDecodeElementSegment(t *testing.T) {
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
Init: []wasm.Index{wasm.ElementInitNullReference, 165675008, wasm.ElementInitNullReference},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
@@ -310,12 +342,12 @@ func TestDecodeElementSegment(t *testing.T) {
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
Init: []wasm.Index{wasm.ElementInitNullReference, 165675008, wasm.ElementInitNullReference},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModePassive,
Type: wasm.RefTypeFuncref,
},
@@ -342,13 +374,13 @@ func TestDecodeElementSegment(t *testing.T) {
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
Init: []wasm.Index{wasm.ElementInitNullReference, 165675008, wasm.ElementInitNullReference},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
},
@@ -366,13 +398,13 @@ func TestDecodeElementSegment(t *testing.T) {
3, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
0x80, 0x7f,
wasm.OpcodeEnd,
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
Init: []wasm.Index{wasm.ElementInitNullReference, 165675008, wasm.ElementInitNullReference},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
Mode: wasm.ElementModeActive,
Type: wasm.RefTypeFuncref,
TableIndex: 10,
@@ -407,11 +439,11 @@ func TestDecodeElementSegment(t *testing.T) {
2, // number of const expr.
wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
wasm.OpcodeRefFunc,
0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
0x80, 0x7f,
wasm.OpcodeEnd,
},
exp: wasm.ElementSegment{
Init: []wasm.Index{wasm.ElementInitNullReference, 165675008},
Init: []wasm.Index{wasm.ElementInitNullReference, 16256},
Mode: wasm.ElementModeDeclarative,
Type: wasm.RefTypeFuncref,
},

View File

@@ -213,10 +213,19 @@ func (m *ModuleInstance) applyElements(elems []ElementSegment) {
references[offset+uint32(i)] = Reference(0)
}
} else {
for i, fnIndex := range elem.Init {
if fnIndex != ElementInitNullReference {
references[offset+uint32(i)] = m.Engine.FunctionInstanceReference(fnIndex)
for i, init := range elem.Init {
if init == ElementInitNullReference {
continue
}
var ref Reference
if index, ok := unwrapElementInitGlobalReference(init); ok {
global := m.Globals[index]
ref = Reference(global.Val)
} else {
ref = m.Engine.FunctionInstanceReference(index)
}
references[offset+uint32(i)] = ref
}
}
}

View File

@@ -933,7 +933,7 @@ func globalsContain(globals []*GlobalInstance, want *GlobalInstance) bool {
return false
}
func TestModuleInstance_applyElementsapplyElements(t *testing.T) {
func TestModuleInstance_applyElements(t *testing.T) {
leb128_100 := leb128.EncodeInt32(100)
t.Run("extenref", func(t *testing.T) {
@@ -962,7 +962,7 @@ func TestModuleInstance_applyElementsapplyElements(t *testing.T) {
me, err := e.NewModuleEngine(nil, nil)
me.(*mockModuleEngine).functionRefs = map[Index]Reference{0: 0xa, 1: 0xaa, 2: 0xaaa, 3: 0xaaaa}
require.NoError(t, err)
m := &ModuleInstance{Engine: me}
m := &ModuleInstance{Engine: me, Globals: []*GlobalInstance{{}, {Val: 0xabcde}}}
m.Tables = []*TableInstance{{Type: RefTypeFuncref, References: make([]Reference, 10)}}
for i := range m.Tables[0].References {
@@ -973,15 +973,16 @@ func TestModuleInstance_applyElementsapplyElements(t *testing.T) {
m.applyElements([]ElementSegment{{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: []Index{1, 2, 3}}})
m.applyElements([]ElementSegment{
{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0, 1, 2}},
{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{9}}, Init: []Index{1 | ElementInitImportedGlobalFunctionReference}},
{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128_100}, Init: make([]Index, 5)}, // Iteration stops at this point, so the offset:5 below shouldn't be applied.
{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: make([]Index, 5)},
})
require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xabcde},
m.Tables[0].References)
m.applyElements([]ElementSegment{
{Mode: ElementModeActive, OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: []Index{0, ElementInitNullReference, 2}},
})
require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xa, 0xffff, 0xaaa, 0xffff, 0xffff},
require.Equal(t, []Reference{0xa, 0xaa, 0xaaa, 0xffff, 0xffff, 0xa, 0xffff, 0xaaa, 0xffff, 0xabcde},
m.Tables[0].References)
})
}

View File

@@ -76,12 +76,32 @@ type ElementSegment struct {
Mode ElementMode
}
// ElementInitNullReference represents the null reference in ElementSegment's Init.
// In Wasm spec, an init item represents either Function's Index or null reference,
// and in wazero, we limit the maximum number of functions available in a module to
// MaximumFunctionIndex. Therefore, it is safe to use math.MaxUint32 to represent the null
// reference in Element segments.
const ElementInitNullReference Index = math.MaxUint32
const (
// ElementInitNullReference represents the null reference in ElementSegment's Init.
// In Wasm spec, an init item represents either Function's Index or null reference,
// and in wazero, we limit the maximum number of functions available in a module to
// MaximumFunctionIndex. Therefore, it is safe to use 1 << 31 to represent the null
// reference in Element segments.
ElementInitNullReference Index = 1 << 31
// ElementInitImportedGlobalFunctionReference represents an init item which is resolved via an imported global constexpr.
// The actual function reference stored at Global is only known at instantiation-time, so we set this flag
// to items of ElementSegment.Init at binary decoding, and unwrap this flag at instantiation to resolve the value.
//
// This might collide the init element resolved via ref.func instruction which is resolved with the func index at decoding,
// but in practice, that is not allowed in wazero thanks to our limit MaximumFunctionIndex. Thus, it is safe to set this flag
// in init element to indicate as such.
ElementInitImportedGlobalFunctionReference Index = 1 << 30
)
// unwrapElementInitGlobalReference takes an item of the init vector of an ElementSegment,
// and returns the Global index if it is supposed to get generated from a global.
// ok is true if the given init item is as such.
func unwrapElementInitGlobalReference(init Index) (_ Index, ok bool) {
if init&ElementInitImportedGlobalFunctionReference == ElementInitImportedGlobalFunctionReference {
return init &^ ElementInitImportedGlobalFunctionReference, true
}
return init, false
}
// IsActive returns true if the element segment is "active" mode which requires the runtime to initialize table
// with the contents in .Init field.
@@ -135,6 +155,7 @@ func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table,
// Create bounds checks as these can err prior to instantiation
funcCount := m.ImportFunctionCount + m.SectionElementCount(SectionIDFunction)
globalsCount := m.ImportGlobalCount + m.SectionElementCount(SectionIDGlobal)
// Now, we have to figure out which table elements can be resolved before instantiation and also fail early if there
// are any imported globals that are known to be invalid by their declarations.
@@ -145,9 +166,19 @@ func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table,
if elem.Type == RefTypeFuncref {
// Any offset applied is to the element, not the function index: validate here if the funcidx is sound.
for ei, funcIdx := range elem.Init {
if funcIdx != ElementInitNullReference && funcIdx >= funcCount {
return fmt.Errorf("%s[%d].init[%d] funcidx %d out of range", SectionIDName(SectionIDElement), idx, ei, funcIdx)
for ei, init := range elem.Init {
if init == ElementInitNullReference {
continue
}
index, ok := unwrapElementInitGlobalReference(init)
if ok {
if index >= globalsCount {
return fmt.Errorf("%s[%d].init[%d] globalidx %d out of range", SectionIDName(SectionIDElement), idx, ei, index)
}
} else {
if index >= funcCount {
return fmt.Errorf("%s[%d].init[%d] funcidx %d out of range", SectionIDName(SectionIDElement), idx, ei, index)
}
}
}
} else {

View File

@@ -528,6 +528,26 @@ func TestModule_validateTable_Errors(t *testing.T) {
},
expectedErr: "element[0].init[1] funcidx 1 out of range",
},
{
name: "constant derived element offset - global out of range",
input: &Module{
ImportGlobalCount: 50,
TypeSection: []FunctionType{{}},
TableSection: []Table{{Min: 1}},
FunctionSection: []Index{0},
CodeSection: []Code{codeEnd},
ElementSection: []ElementSegment{
{
OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{
ElementInitImportedGlobalFunctionReference | 1,
ElementInitImportedGlobalFunctionReference | 100,
},
Type: RefTypeFuncref,
},
},
},
expectedErr: "element[0].init[1] globalidx 100 out of range",
},
{
name: "imported global derived element offset - missing table",
input: &Module{
@@ -1052,3 +1072,20 @@ func TestTableInstance_Grow(t *testing.T) {
})
}
}
func Test_unwrapElementInitGlobalReference(t *testing.T) {
actual, ok := unwrapElementInitGlobalReference(12345 | ElementInitImportedGlobalFunctionReference)
require.True(t, ok)
require.Equal(t, actual, uint32(12345))
actual, ok = unwrapElementInitGlobalReference(12345)
require.False(t, ok)
require.Equal(t, actual, uint32(12345))
}
// Test_ElementInitSpecials ensures these special consts are larger than MaximumFunctionIndex so that
// they won't collide with the actual index.
func Test_ElementInitSpecials(t *testing.T) {
require.True(t, ElementInitNullReference > MaximumFunctionIndex)
require.True(t, ElementInitImportedGlobalFunctionReference > MaximumFunctionIndex)
}