1 package wasm
2
3 import (
4 "math"
5 "testing"
6
7 "github.com/tetratelabs/wazero/api"
8 "github.com/tetratelabs/wazero/internal/leb128"
9 "github.com/tetratelabs/wazero/internal/testing/require"
10 )
11
12
13
14 func Test_ElementInitNullReference_valid(t *testing.T) {
15 require.True(t, MaximumFunctionIndex < ElementInitNullReference)
16 }
17
18 func Test_resolveImports_table(t *testing.T) {
19 const moduleName = "test"
20 const name = "target"
21
22 t.Run("ok", func(t *testing.T) {
23 max := uint32(10)
24 tableInst := &TableInstance{Max: &max}
25 s := newStore()
26 s.nameToModule[moduleName] = &ModuleInstance{
27 Tables: []*TableInstance{tableInst},
28 Exports: map[string]*Export{name: {Type: ExternTypeTable, Index: 0}},
29 ModuleName: moduleName,
30 }
31 m := &ModuleInstance{Tables: make([]*TableInstance, 1), s: s}
32 err := m.resolveImports(&Module{
33 ImportPerModule: map[string][]*Import{
34 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: Table{Max: &max}}},
35 },
36 })
37 require.NoError(t, err)
38 require.Equal(t, m.Tables[0], tableInst)
39 })
40 t.Run("minimum size mismatch", func(t *testing.T) {
41 s := newStore()
42 importTableType := Table{Min: 2}
43 s.nameToModule[moduleName] = &ModuleInstance{
44 Tables: []*TableInstance{{Min: importTableType.Min - 1}},
45 Exports: map[string]*Export{name: {Type: ExternTypeTable}},
46 ModuleName: moduleName,
47 }
48 m := &ModuleInstance{Tables: make([]*TableInstance, 1), s: s}
49 err := m.resolveImports(&Module{
50 ImportPerModule: map[string][]*Import{
51 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}},
52 },
53 })
54 require.EqualError(t, err, "import table[test.target]: minimum size mismatch: 2 > 1")
55 })
56 t.Run("maximum size mismatch", func(t *testing.T) {
57 max := uint32(10)
58 importTableType := Table{Max: &max}
59 s := newStore()
60 s.nameToModule[moduleName] = &ModuleInstance{
61 Tables: []*TableInstance{{Min: importTableType.Min - 1}},
62 Exports: map[string]*Export{name: {Type: ExternTypeTable}},
63 ModuleName: moduleName,
64 }
65 m := &ModuleInstance{Tables: make([]*TableInstance, 1), s: s}
66 err := m.resolveImports(&Module{
67 ImportPerModule: map[string][]*Import{
68 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: importTableType}},
69 },
70 })
71 require.EqualError(t, err, "import table[test.target]: maximum size mismatch: 10, but actual has no max")
72 })
73 t.Run("type mismatch", func(t *testing.T) {
74 s := newStore()
75 s.nameToModule[moduleName] = &ModuleInstance{
76 Tables: []*TableInstance{{Type: RefTypeFuncref}},
77 Exports: map[string]*Export{name: {Type: ExternTypeTable}},
78 ModuleName: moduleName,
79 }
80 m := &ModuleInstance{Tables: make([]*TableInstance, 1), s: s}
81 err := m.resolveImports(&Module{
82 ImportPerModule: map[string][]*Import{
83 moduleName: {{Module: moduleName, Name: name, Type: ExternTypeTable, DescTable: Table{Type: RefTypeExternref}}},
84 },
85 })
86 require.EqualError(t, err, "import table[test.target]: table type mismatch: externref != funcref")
87 })
88 }
89
90 var codeEnd = Code{Body: []byte{OpcodeEnd}}
91
92 func TestModule_validateTable(t *testing.T) {
93 const maxTableIndex = 5
94 three := uint32(3)
95 tests := []struct {
96 name string
97 input *Module
98 }{
99 {
100 name: "empty",
101 input: &Module{},
102 },
103 {
104 name: "min zero",
105 input: &Module{TableSection: []Table{{}}},
106 },
107 {
108 name: "maximum number of tables",
109 input: &Module{TableSection: []Table{{}, {}, {}, {}, {}}},
110 },
111 {
112 name: "min/max",
113 input: &Module{TableSection: []Table{{Min: 1, Max: &three}}},
114 },
115 {
116 name: "constant derived element offset=0 and no index",
117 input: &Module{
118 TypeSection: []FunctionType{{}},
119 TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
120 FunctionSection: []Index{0},
121 CodeSection: []Code{codeEnd},
122 ElementSection: []ElementSegment{
123 {
124 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
125 Type: RefTypeFuncref,
126 },
127 },
128 },
129 },
130 {
131 name: "constant derived element offset=0 and one index",
132 input: &Module{
133 TypeSection: []FunctionType{{}},
134 TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
135 FunctionSection: []Index{0},
136 CodeSection: []Code{codeEnd},
137 ElementSection: []ElementSegment{
138 {
139 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
140 Init: []Index{0},
141 Type: RefTypeFuncref,
142 },
143 },
144 },
145 },
146 {
147 name: "constant derived element offset - ignores min on imported table",
148 input: &Module{
149 ImportTableCount: 1,
150 TypeSection: []FunctionType{{}},
151 ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Type: RefTypeFuncref}}},
152 FunctionSection: []Index{0},
153 CodeSection: []Code{codeEnd},
154 ElementSection: []ElementSegment{
155 {
156 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
157 Init: []Index{0},
158 Type: RefTypeFuncref,
159 },
160 },
161 },
162 },
163 {
164 name: "constant derived element offset=0 and one index - imported table",
165 input: &Module{
166 TypeSection: []FunctionType{{}},
167 ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1, Type: RefTypeFuncref}}},
168 FunctionSection: []Index{0},
169 CodeSection: []Code{codeEnd},
170 ElementSection: []ElementSegment{
171 {
172 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0},
173 Init: []Index{0},
174 Type: RefTypeFuncref,
175 },
176 },
177 },
178 },
179 {
180 name: "constant derived element offset and two indices",
181 input: &Module{
182 TypeSection: []FunctionType{{}},
183 TableSection: []Table{{Min: 3, Type: RefTypeFuncref}},
184 FunctionSection: []Index{0, 0, 0, 0},
185 CodeSection: []Code{codeEnd, codeEnd, codeEnd, codeEnd},
186 ElementSection: []ElementSegment{
187 {
188 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
189 Init: []Index{0, 2},
190 Type: RefTypeFuncref,
191 },
192 },
193 },
194 },
195 {
196 name: "imported global derived element offset and no index",
197 input: &Module{
198 TypeSection: []FunctionType{{}},
199 ImportSection: []Import{
200 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
201 },
202 TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
203 FunctionSection: []Index{0},
204 CodeSection: []Code{codeEnd},
205 ElementSection: []ElementSegment{
206 {
207 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
208 Type: RefTypeFuncref,
209 },
210 },
211 },
212 },
213 {
214 name: "imported global derived element offset and one index",
215 input: &Module{
216 TypeSection: []FunctionType{{}},
217 ImportSection: []Import{
218 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
219 },
220 TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
221 FunctionSection: []Index{0},
222 CodeSection: []Code{codeEnd},
223 ElementSection: []ElementSegment{
224 {
225 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
226 Init: []Index{0},
227 Type: RefTypeFuncref,
228 },
229 },
230 },
231 },
232 {
233 name: "imported global derived element offset and one index - imported table",
234 input: &Module{
235 TypeSection: []FunctionType{{}},
236 ImportSection: []Import{
237 {Type: ExternTypeTable, DescTable: Table{Min: 1, Type: RefTypeFuncref}},
238 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
239 },
240 FunctionSection: []Index{0},
241 CodeSection: []Code{codeEnd},
242 ElementSection: []ElementSegment{
243 {
244 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
245 Init: []Index{0},
246 Type: RefTypeFuncref,
247 },
248 },
249 },
250 },
251 {
252 name: "imported global derived element offset - ignores min on imported table",
253 input: &Module{
254 TypeSection: []FunctionType{{}},
255 ImportSection: []Import{
256 {Type: ExternTypeTable, DescTable: Table{Type: RefTypeFuncref}},
257 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
258 },
259 FunctionSection: []Index{0},
260 CodeSection: []Code{codeEnd},
261 ElementSection: []ElementSegment{
262 {
263 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
264 Init: []Index{0},
265 Type: RefTypeFuncref,
266 },
267 },
268 },
269 },
270 {
271 name: "imported global derived element offset - two indices",
272 input: &Module{
273 TypeSection: []FunctionType{{}},
274 ImportSection: []Import{
275 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
276 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
277 },
278 TableSection: []Table{{Min: 3, Type: RefTypeFuncref}},
279 FunctionSection: []Index{0, 0, 0, 0},
280 CodeSection: []Code{codeEnd, codeEnd, codeEnd, codeEnd},
281 ElementSection: []ElementSegment{
282 {
283 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
284 Init: []Index{0, 2},
285 Type: RefTypeFuncref,
286 },
287 },
288 },
289 },
290 {
291 name: "mixed elementSegments - const before imported global",
292 input: &Module{
293 TypeSection: []FunctionType{{}},
294 ImportSection: []Import{
295 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
296 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
297 },
298 TableSection: []Table{{Min: 3, Type: RefTypeFuncref}},
299 FunctionSection: []Index{0, 0, 0, 0},
300 CodeSection: []Code{codeEnd, codeEnd, codeEnd, codeEnd},
301 ElementSection: []ElementSegment{
302 {
303 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
304 Init: []Index{0, 2},
305 Type: RefTypeFuncref,
306 },
307 {
308 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
309 Init: []Index{1, 2},
310 Type: RefTypeFuncref,
311 },
312 },
313 },
314 },
315 }
316
317 for _, tt := range tests {
318 tc := tt
319
320 t.Run(tc.name, func(t *testing.T) {
321 _, _, _, tables, err := tc.input.AllDeclarations()
322 require.NoError(t, err)
323
324 err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
325 require.NoError(t, err)
326
327 err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
328 require.NoError(t, err)
329 })
330 }
331 }
332
333 func TestModule_validateTable_Errors(t *testing.T) {
334 const maxTableIndex = 5
335 tests := []struct {
336 name string
337 input *Module
338 expectedErr string
339 }{
340 {
341 name: "too many tables",
342 input: &Module{
343 TableSection: []Table{{}, {}, {}, {}, {}, {}},
344 },
345 expectedErr: "too many tables in a module: 6 given with limit 5",
346 },
347 {
348 name: "type mismatch: unknown ref type",
349 input: &Module{
350 TableSection: []Table{{Type: RefTypeFuncref}},
351 ElementSection: []ElementSegment{
352 {
353 OffsetExpr: ConstantExpression{
354 Opcode: OpcodeI32Const,
355 Data: leb128.EncodeUint64(math.MaxUint64),
356 },
357 Type: 0xff,
358 },
359 },
360 },
361 expectedErr: "element type mismatch: table has funcref but element has unknown(0xff)",
362 },
363 {
364 name: "type mismatch: funcref elem on extern table",
365 input: &Module{
366 TableSection: []Table{{Type: RefTypeExternref}},
367 ElementSection: []ElementSegment{
368 {
369 OffsetExpr: ConstantExpression{
370 Opcode: OpcodeI32Const,
371 Data: leb128.EncodeUint64(math.MaxUint64),
372 },
373 Type: RefTypeFuncref,
374 },
375 },
376 },
377 expectedErr: "element type mismatch: table has externref but element has funcref",
378 },
379 {
380 name: "type mismatch: extern elem on funcref table",
381 input: &Module{
382 TableSection: []Table{{Type: RefTypeFuncref}},
383 ElementSection: []ElementSegment{
384 {
385 OffsetExpr: ConstantExpression{
386 Opcode: OpcodeI32Const,
387 Data: leb128.EncodeUint64(math.MaxUint64),
388 },
389 Type: RefTypeExternref,
390 },
391 },
392 },
393 expectedErr: "element type mismatch: table has funcref but element has externref",
394 },
395 {
396 name: "non-nil externref",
397 input: &Module{
398 TableSection: []Table{{Type: RefTypeFuncref}},
399 ElementSection: []ElementSegment{
400 {
401 OffsetExpr: ConstantExpression{
402 Opcode: OpcodeI32Const,
403 Data: leb128.EncodeUint64(math.MaxUint64),
404 },
405 Type: RefTypeExternref,
406 Init: []Index{0},
407 },
408 },
409 },
410 expectedErr: "element[0].init[0] must be ref.null but was 0",
411 },
412 {
413 name: "constant derived element offset - decode error",
414 input: &Module{
415 TypeSection: []FunctionType{{}},
416 TableSection: []Table{{Type: RefTypeFuncref}},
417 FunctionSection: []Index{0},
418 CodeSection: []Code{codeEnd},
419 ElementSection: []ElementSegment{
420 {
421 OffsetExpr: ConstantExpression{
422 Opcode: OpcodeI32Const,
423 Data: leb128.EncodeUint64(math.MaxUint64),
424 },
425 Init: []Index{0},
426 Type: RefTypeFuncref,
427 },
428 },
429 },
430 expectedErr: "element[0] couldn't read i32.const parameter: overflows a 32-bit integer",
431 },
432 {
433 name: "constant derived element offset - wrong ValType",
434 input: &Module{
435 TypeSection: []FunctionType{{}},
436 TableSection: []Table{{Type: RefTypeFuncref}},
437 FunctionSection: []Index{0},
438 CodeSection: []Code{codeEnd},
439 ElementSection: []ElementSegment{
440 {
441 OffsetExpr: ConstantExpression{Opcode: OpcodeI64Const, Data: const0}, Init: []Index{0},
442 Type: RefTypeFuncref,
443 },
444 },
445 },
446 expectedErr: "element[0] has an invalid const expression: i64.const",
447 },
448 {
449 name: "constant derived element offset - missing table",
450 input: &Module{
451 TypeSection: []FunctionType{{}},
452 FunctionSection: []Index{0},
453 CodeSection: []Code{codeEnd},
454 ElementSection: []ElementSegment{
455 {
456 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, Init: []Index{0},
457 Type: RefTypeFuncref,
458 },
459 },
460 },
461 expectedErr: "unknown table 0 as active element target",
462 },
463 {
464 name: "constant derived element offset exceeds table min",
465 input: &Module{
466 TypeSection: []FunctionType{{}},
467 TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
468 FunctionSection: []Index{0},
469 CodeSection: []Code{codeEnd},
470 ElementSection: []ElementSegment{
471 {
472 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)}, Init: []Index{0},
473 Type: RefTypeFuncref,
474 },
475 },
476 },
477 expectedErr: "element[0].init exceeds min table size",
478 },
479 {
480 name: "constant derived element offset puts init beyond table min",
481 input: &Module{
482 TypeSection: []FunctionType{{}},
483 TableSection: []Table{{Min: 2, Type: RefTypeFuncref}},
484 FunctionSection: []Index{0},
485 CodeSection: []Code{codeEnd},
486 ElementSection: []ElementSegment{
487 {
488 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{0},
489 Type: RefTypeFuncref,
490 },
491 {
492 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{0, 0},
493 Type: RefTypeFuncref,
494 },
495 },
496 },
497 expectedErr: "element[1].init exceeds min table size",
498 },
499 {
500 name: "constant derived element offset beyond table min - no init elements",
501 input: &Module{
502 TypeSection: []FunctionType{{}},
503 TableSection: []Table{{Min: 1, Type: RefTypeFuncref}},
504 FunctionSection: []Index{0},
505 CodeSection: []Code{codeEnd},
506 ElementSection: []ElementSegment{
507 {
508 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(2)},
509 Type: RefTypeFuncref,
510 },
511 },
512 },
513 expectedErr: "element[0].init exceeds min table size",
514 },
515 {
516 name: "constant derived element offset - funcidx out of range",
517 input: &Module{
518 TypeSection: []FunctionType{{}},
519 TableSection: []Table{{Min: 1}},
520 FunctionSection: []Index{0},
521 CodeSection: []Code{codeEnd},
522 ElementSection: []ElementSegment{
523 {
524 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{0, 1},
525 Type: RefTypeFuncref,
526 },
527 },
528 },
529 expectedErr: "element[0].init[1] funcidx 1 out of range",
530 },
531 {
532 name: "constant derived element offset - global out of range",
533 input: &Module{
534 ImportGlobalCount: 50,
535 TypeSection: []FunctionType{{}},
536 TableSection: []Table{{Min: 1}},
537 FunctionSection: []Index{0},
538 CodeSection: []Code{codeEnd},
539 ElementSection: []ElementSegment{
540 {
541 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: const1}, Init: []Index{
542 ElementInitImportedGlobalFunctionReference | 1,
543 ElementInitImportedGlobalFunctionReference | 100,
544 },
545 Type: RefTypeFuncref,
546 },
547 },
548 },
549 expectedErr: "element[0].init[1] globalidx 100 out of range",
550 },
551 {
552 name: "imported global derived element offset - missing table",
553 input: &Module{
554 TypeSection: []FunctionType{{}},
555 ImportSection: []Import{
556 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
557 },
558 FunctionSection: []Index{0},
559 CodeSection: []Code{codeEnd},
560 ElementSection: []ElementSegment{
561 {
562 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []Index{0},
563 Type: RefTypeFuncref,
564 },
565 },
566 },
567 expectedErr: "unknown table 0 as active element target",
568 },
569 {
570 name: "imported global derived element offset - funcidx out of range",
571 input: &Module{
572 TypeSection: []FunctionType{{}},
573 ImportSection: []Import{
574 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
575 },
576 TableSection: []Table{{Min: 1}},
577 FunctionSection: []Index{0},
578 CodeSection: []Code{codeEnd},
579 ElementSection: []ElementSegment{
580 {
581 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []Index{0, 1},
582 Type: RefTypeFuncref,
583 },
584 },
585 },
586 expectedErr: "element[0].init[1] funcidx 1 out of range",
587 },
588 {
589 name: "imported global derived element offset - wrong ValType",
590 input: &Module{
591 TypeSection: []FunctionType{{}},
592 ImportSection: []Import{
593 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
594 },
595 TableSection: []Table{{Type: RefTypeFuncref}},
596 FunctionSection: []Index{0},
597 CodeSection: []Code{codeEnd},
598 ElementSection: []ElementSegment{
599 {
600 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []Index{0},
601 Type: RefTypeFuncref,
602 },
603 },
604 },
605 expectedErr: "element[0] (global.get 0): import[0].global.ValType != i32",
606 },
607 {
608 name: "imported global derived element offset - decode error",
609 input: &Module{
610 TypeSection: []FunctionType{{}},
611 ImportSection: []Import{
612 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
613 },
614 TableSection: []Table{{Type: RefTypeFuncref}},
615 FunctionSection: []Index{0},
616 CodeSection: []Code{codeEnd},
617 ElementSection: []ElementSegment{
618 {
619 OffsetExpr: ConstantExpression{
620 Opcode: OpcodeGlobalGet,
621 Data: leb128.EncodeUint64(math.MaxUint64),
622 },
623 Init: []Index{0},
624 Type: RefTypeFuncref,
625 },
626 },
627 },
628 expectedErr: "element[0] couldn't read global.get parameter: overflows a 32-bit integer",
629 },
630 {
631 name: "imported global derived element offset - no imports",
632 input: &Module{
633 TypeSection: []FunctionType{{}},
634 TableSection: []Table{{Type: RefTypeFuncref}},
635 FunctionSection: []Index{0},
636 GlobalSection: []Global{{Type: GlobalType{ValType: ValueTypeI32}}},
637 CodeSection: []Code{codeEnd},
638 ElementSection: []ElementSegment{
639 {
640 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []Index{0},
641 Type: RefTypeFuncref,
642 },
643 },
644 },
645 expectedErr: "element[0] (global.get 0): out of range of imported globals",
646 },
647 {
648 name: "imported global derived element offset - no imports are globals",
649 input: &Module{
650 TypeSection: []FunctionType{{}},
651 ImportSection: []Import{
652 {Type: ExternTypeFunc, DescFunc: 0},
653 },
654 TableSection: []Table{{Type: RefTypeFuncref}},
655 FunctionSection: []Index{0},
656 GlobalSection: []Global{{Type: GlobalType{ValType: ValueTypeI32}}},
657 CodeSection: []Code{codeEnd},
658 ElementSection: []ElementSegment{
659 {
660 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}}, Init: []Index{0},
661 Type: RefTypeFuncref,
662 },
663 },
664 },
665 expectedErr: "element[0] (global.get 0): out of range of imported globals",
666 },
667 }
668
669 for _, tt := range tests {
670 tc := tt
671
672 t.Run(tc.name, func(t *testing.T) {
673 _, _, _, tables, err := tc.input.AllDeclarations()
674 require.NoError(t, err)
675 err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
676 require.EqualError(t, err, tc.expectedErr)
677 })
678 }
679 }
680
681 var (
682 const0 = leb128.EncodeInt32(0)
683 const1 = leb128.EncodeInt32(1)
684 )
685
686 func TestModule_buildTables(t *testing.T) {
687 three := uint32(3)
688 tests := []struct {
689 name string
690 module *Module
691 importedTables []*TableInstance
692 importedGlobals []*GlobalInstance
693 expectedTables []*TableInstance
694 }{
695 {
696 name: "empty",
697 module: &Module{
698 ElementSection: []ElementSegment{},
699 },
700 },
701 {
702 name: "min zero",
703 module: &Module{
704 TableSection: []Table{{Type: RefTypeFuncref}},
705 ElementSection: []ElementSegment{},
706 },
707 expectedTables: []*TableInstance{{References: make([]Reference, 0), Min: 0, Type: RefTypeFuncref}},
708 },
709 {
710 name: "min/max",
711 module: &Module{
712 TableSection: []Table{{Min: 1, Max: &three}},
713 ElementSection: []ElementSegment{},
714 },
715 expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1, Max: &three}},
716 },
717 {
718 name: "constant derived element offset=0 and no index",
719 module: &Module{
720 TypeSection: []FunctionType{{}},
721 TableSection: []Table{{Min: 1}},
722 FunctionSection: []Index{0},
723 CodeSection: []Code{codeEnd},
724 ElementSection: []ElementSegment{},
725 },
726 expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
727 },
728 {
729 name: "null extern refs",
730 module: &Module{
731 TableSection: []Table{{Min: 10, Type: RefTypeExternref}},
732 ElementSection: []ElementSegment{
733 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{5}}, Init: []Index{ElementInitNullReference, ElementInitNullReference, ElementInitNullReference}},
734 },
735 },
736 expectedTables: []*TableInstance{{References: make([]Reference, 10), Min: 10, Type: RefTypeExternref}},
737 },
738 {
739 name: "constant derived element offset=0 and one index",
740 module: &Module{
741 TypeSection: []FunctionType{{}},
742 TableSection: []Table{{Min: 1}},
743 FunctionSection: []Index{0},
744 CodeSection: []Code{codeEnd},
745 ElementSection: []ElementSegment{
746 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
747 },
748 },
749 expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
750 },
751 {
752 name: "constant derived element offset - imported table",
753 module: &Module{
754 TypeSection: []FunctionType{{}},
755 FunctionSection: []Index{0},
756 CodeSection: []Code{codeEnd},
757 ElementSection: []ElementSegment{
758 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
759 },
760 },
761 importedTables: []*TableInstance{{Min: 2}},
762 expectedTables: []*TableInstance{{Min: 2}},
763 },
764 {
765 name: "constant derived element offset=0 and one index - imported table",
766 module: &Module{
767 TypeSection: []FunctionType{{}},
768 ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1}}},
769 FunctionSection: []Index{0},
770 CodeSection: []Code{codeEnd},
771 ElementSection: []ElementSegment{
772 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
773 },
774 },
775 importedTables: []*TableInstance{{Min: 1}},
776 expectedTables: []*TableInstance{{Min: 1}},
777 },
778 {
779 name: "constant derived element offset and two indices",
780 module: &Module{
781 TypeSection: []FunctionType{{}},
782 TableSection: []Table{{Min: 3}},
783 FunctionSection: []Index{0, 0, 0, 0},
784 CodeSection: []Code{codeEnd, codeEnd, codeEnd, codeEnd},
785 ElementSection: []ElementSegment{
786 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}}, Init: []Index{0, 2}},
787 },
788 },
789 expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
790 },
791 {
792 name: "imported global derived element offset and no index",
793 module: &Module{
794 TypeSection: []FunctionType{{}},
795 ImportSection: []Import{
796 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
797 },
798 TableSection: []Table{{Min: 1}},
799 FunctionSection: []Index{0},
800 CodeSection: []Code{codeEnd},
801 ElementSection: []ElementSegment{},
802 },
803 importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
804 expectedTables: []*TableInstance{{References: make([]Reference, 1), Min: 1}},
805 },
806 {
807 name: "imported global derived element offset and one index",
808 module: &Module{
809 TypeSection: []FunctionType{{}},
810 ImportSection: []Import{
811 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
812 },
813 TableSection: []Table{{Min: 2}},
814 FunctionSection: []Index{0},
815 CodeSection: []Code{codeEnd},
816 ElementSection: []ElementSegment{
817 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
818 },
819 },
820 importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
821 expectedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
822 },
823 {
824 name: "imported global derived element offset and one index - imported table",
825 module: &Module{
826 TypeSection: []FunctionType{{}},
827 ImportSection: []Import{
828 {Type: ExternTypeTable, DescTable: Table{Min: 1}},
829 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
830 },
831 FunctionSection: []Index{0},
832 CodeSection: []Code{codeEnd},
833 ElementSection: []ElementSegment{
834 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
835 },
836 },
837 importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
838 importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
839 expectedTables: []*TableInstance{{Min: 2, References: []Reference{0, 0}}},
840 },
841 {
842 name: "imported global derived element offset - ignores min on imported table",
843 module: &Module{
844 TypeSection: []FunctionType{{}},
845 ImportSection: []Import{
846 {Type: ExternTypeTable, DescTable: Table{}},
847 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
848 },
849 FunctionSection: []Index{0},
850 CodeSection: []Code{codeEnd},
851 ElementSection: []ElementSegment{
852 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{0}}, Init: []Index{0}},
853 },
854 },
855 importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 1}},
856 importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
857 expectedTables: []*TableInstance{{Min: 2, References: []Reference{0, 0}}},
858 },
859 {
860 name: "imported global derived element offset - two indices",
861 module: &Module{
862 TypeSection: []FunctionType{{}},
863 ImportSection: []Import{
864 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
865 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
866 },
867 TableSection: []Table{{Min: 3}, {Min: 100}},
868 FunctionSection: []Index{0, 0, 0, 0},
869 CodeSection: []Code{codeEnd, codeEnd, codeEnd, codeEnd},
870 ElementSection: []ElementSegment{
871 {
872 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
873 Init: []Index{ElementInitNullReference, 2},
874 TableIndex: 1,
875 },
876 {
877 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x1}},
878 Init: []Index{0, 2},
879 TableIndex: 0,
880 },
881 },
882 },
883 importedGlobals: []*GlobalInstance{
884 {Type: GlobalType{ValType: ValueTypeI64}, Val: 3},
885 {Type: GlobalType{ValType: ValueTypeI32}, Val: 1},
886 },
887 expectedTables: []*TableInstance{
888 {References: make([]Reference, 3), Min: 3},
889 {References: make([]Reference, 100), Min: 100},
890 },
891 },
892 {
893 name: "mixed elementSegments - const before imported global",
894 module: &Module{
895 TypeSection: []FunctionType{{}},
896 ImportSection: []Import{
897 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI64}},
898 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
899 },
900 TableSection: []Table{{Min: 3}},
901 FunctionSection: []Index{0, 0, 0, 0},
902 CodeSection: []Code{codeEnd, codeEnd, codeEnd, codeEnd},
903 ElementSection: []ElementSegment{
904 {OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{1}}, Init: []Index{0, 2}},
905 {OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}}, Init: []Index{1, 2}},
906 },
907 },
908 importedGlobals: []*GlobalInstance{
909 {Type: GlobalType{ValType: ValueTypeI64}, Val: 3},
910 {Type: GlobalType{ValType: ValueTypeI32}, Val: 1},
911 },
912 expectedTables: []*TableInstance{{References: make([]Reference, 3), Min: 3}},
913 },
914 }
915
916 for _, tt := range tests {
917 tc := tt
918
919 t.Run(tc.name, func(t *testing.T) {
920 m := &ModuleInstance{
921 Tables: append(tc.importedTables, make([]*TableInstance, len(tc.module.TableSection))...),
922 Globals: tc.importedGlobals,
923 }
924 err := m.buildTables(tc.module, false)
925 require.NoError(t, err)
926
927 require.Equal(t, tc.expectedTables, m.Tables)
928 })
929 }
930 }
931
932
933 func TestModule_buildTable_Errors(t *testing.T) {
934 tests := []struct {
935 name string
936 module *Module
937 importedTables []*TableInstance
938 importedGlobals []*GlobalInstance
939 expectedErr string
940 }{
941 {
942 name: "constant derived element offset exceeds table min - imported table",
943 module: &Module{
944 TypeSection: []FunctionType{{}},
945 ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{}}},
946 FunctionSection: []Index{0},
947 CodeSection: []Code{codeEnd},
948 ElementSection: []ElementSegment{
949 {
950 OffsetExpr: ConstantExpression{Opcode: OpcodeI32Const, Data: []byte{2}},
951 Init: []Index{0},
952 },
953 },
954 },
955 importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
956 expectedErr: "element[0].init exceeds min table size",
957 },
958 {
959 name: "imported global derived element offset exceeds table min",
960 module: &Module{
961 TypeSection: []FunctionType{{}},
962 ImportSection: []Import{
963 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
964 },
965 TableSection: []Table{{Min: 2}},
966 FunctionSection: []Index{0},
967 CodeSection: []Code{codeEnd},
968 ElementSection: []ElementSegment{
969 {
970 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
971 Init: []Index{0},
972 },
973 },
974 },
975 importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 2}},
976 expectedErr: "element[0].init exceeds min table size",
977 },
978 {
979 name: "imported global derived element offset exceeds table min imported table",
980 module: &Module{
981 TypeSection: []FunctionType{{}},
982 ImportSection: []Import{
983 {Type: ExternTypeTable, DescTable: Table{}},
984 {Type: ExternTypeGlobal, DescGlobal: GlobalType{ValType: ValueTypeI32}},
985 },
986 TableSection: []Table{{Min: 2}},
987 FunctionSection: []Index{0},
988 CodeSection: []Code{codeEnd},
989 ElementSection: []ElementSegment{
990 {
991 OffsetExpr: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0x0}},
992 Init: []Index{0},
993 },
994 },
995 },
996 importedTables: []*TableInstance{{References: make([]Reference, 2), Min: 2}},
997 importedGlobals: []*GlobalInstance{{Type: GlobalType{ValType: ValueTypeI32}, Val: 2}},
998 expectedErr: "element[0].init exceeds min table size",
999 },
1000 }
1001
1002 for _, tt := range tests {
1003 tc := tt
1004
1005 t.Run(tc.name, func(t *testing.T) {
1006 m := &ModuleInstance{
1007 Tables: append(tc.importedTables, make([]*TableInstance, len(tc.module.TableSection))...),
1008 Globals: tc.importedGlobals,
1009 }
1010 err := m.buildTables(tc.module, false)
1011 require.EqualError(t, err, tc.expectedErr)
1012 })
1013 }
1014 }
1015
1016 func TestTableInstance_Grow(t *testing.T) {
1017 expOnErr := uint32(0xffff_ffff)
1018 max10 := uint32(10)
1019 tests := []struct {
1020 name string
1021 currentLen int
1022 max *uint32
1023 delta, exp uint32
1024 }{
1025 {
1026 name: "growing ousside 32-bit range",
1027 currentLen: 0x10,
1028 delta: 0xffff_fff0,
1029 exp: expOnErr,
1030 },
1031 {
1032 name: "growing zero",
1033 currentLen: 0,
1034 delta: 0,
1035 exp: 0,
1036 },
1037 {
1038 name: "growing zero on non zero table",
1039 currentLen: 5,
1040 delta: 0,
1041 exp: 5,
1042 },
1043 {
1044 name: "grow zero on max",
1045 currentLen: 10,
1046 delta: 0,
1047 max: &max10,
1048 exp: 10,
1049 },
1050 {
1051 name: "grow out of range beyond max",
1052 currentLen: 10,
1053 delta: 1,
1054 max: &max10,
1055 exp: expOnErr,
1056 },
1057 {
1058 name: "grow out of range beyond max part2",
1059 currentLen: 10,
1060 delta: 100,
1061 max: &max10,
1062 exp: expOnErr,
1063 },
1064 }
1065
1066 for _, tt := range tests {
1067 tc := tt
1068 t.Run(tc.name, func(t *testing.T) {
1069 table := &TableInstance{References: make([]uintptr, tc.currentLen), Max: tc.max}
1070 actual := table.Grow(tc.delta, 0)
1071 require.Equal(t, tc.exp, actual)
1072 })
1073 }
1074 }
1075
1076 func Test_unwrapElementInitGlobalReference(t *testing.T) {
1077 actual, ok := unwrapElementInitGlobalReference(12345 | ElementInitImportedGlobalFunctionReference)
1078 require.True(t, ok)
1079 require.Equal(t, actual, uint32(12345))
1080
1081 actual, ok = unwrapElementInitGlobalReference(12345)
1082 require.False(t, ok)
1083 require.Equal(t, actual, uint32(12345))
1084 }
1085
1086
1087
1088 func Test_ElementInitSpecials(t *testing.T) {
1089 require.True(t, ElementInitNullReference > MaximumFunctionIndex)
1090 require.True(t, ElementInitImportedGlobalFunctionReference > MaximumFunctionIndex)
1091 }
1092
View as plain text