Updates Spectest to the latest (May 23, 2023) (#1490)
Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user