github.com/tetratelabs/wazero@v1.2.1/internal/wasm/module_test.go (about) 1 package wasm 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 8 "github.com/tetratelabs/wazero/api" 9 "github.com/tetratelabs/wazero/internal/leb128" 10 "github.com/tetratelabs/wazero/internal/testing/require" 11 "github.com/tetratelabs/wazero/internal/u64" 12 ) 13 14 func TestFunctionType_String(t *testing.T) { 15 tests := []struct { 16 functype *FunctionType 17 exp string 18 }{ 19 {functype: &FunctionType{}, exp: "v_v"}, 20 {functype: &FunctionType{Params: []ValueType{ValueTypeI32}}, exp: "i32_v"}, 21 {functype: &FunctionType{Params: []ValueType{ValueTypeI32, ValueTypeF64}}, exp: "i32f64_v"}, 22 {functype: &FunctionType{Params: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "f32i32f64_v"}, 23 {functype: &FunctionType{Results: []ValueType{ValueTypeI64}}, exp: "v_i64"}, 24 {functype: &FunctionType{Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "v_i64f32"}, 25 {functype: &FunctionType{Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "v_f32i32f64"}, 26 {functype: &FunctionType{Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI64}}, exp: "i32_i64"}, 27 {functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32}, Results: []ValueType{ValueTypeI64, ValueTypeF32}}, exp: "i64f32_i64f32"}, 28 {functype: &FunctionType{Params: []ValueType{ValueTypeI64, ValueTypeF32, ValueTypeF64}, Results: []ValueType{ValueTypeF32, ValueTypeI32, ValueTypeF64}}, exp: "i64f32f64_f32i32f64"}, 29 } 30 31 for _, tt := range tests { 32 tc := tt 33 t.Run(tc.functype.String(), func(t *testing.T) { 34 require.Equal(t, tc.exp, tc.functype.String()) 35 require.Equal(t, tc.exp, tc.functype.key()) 36 require.Equal(t, tc.exp, tc.functype.string) 37 }) 38 } 39 } 40 41 func TestSectionIDName(t *testing.T) { 42 tests := []struct { 43 name string 44 input SectionID 45 expected string 46 }{ 47 {"custom", SectionIDCustom, "custom"}, 48 {"type", SectionIDType, "type"}, 49 {"import", SectionIDImport, "import"}, 50 {"function", SectionIDFunction, "function"}, 51 {"table", SectionIDTable, "table"}, 52 {"memory", SectionIDMemory, "memory"}, 53 {"global", SectionIDGlobal, "global"}, 54 {"export", SectionIDExport, "export"}, 55 {"start", SectionIDStart, "start"}, 56 {"element", SectionIDElement, "element"}, 57 {"code", SectionIDCode, "code"}, 58 {"data", SectionIDData, "data"}, 59 {"unknown", 100, "unknown"}, 60 } 61 62 for _, tt := range tests { 63 tc := tt 64 65 t.Run(tc.name, func(t *testing.T) { 66 require.Equal(t, tc.expected, SectionIDName(tc.input)) 67 }) 68 } 69 } 70 71 func TestMemory_Validate(t *testing.T) { 72 tests := []struct { 73 name string 74 mem *Memory 75 expectedErr string 76 }{ 77 { 78 name: "ok", 79 mem: &Memory{Min: 2, Cap: 2, Max: 2}, 80 }, 81 { 82 name: "cap < min", 83 mem: &Memory{Min: 2, Cap: 1, Max: 2}, 84 expectedErr: "capacity 1 pages (64 Ki) less than minimum 2 pages (128 Ki)", 85 }, 86 { 87 name: "cap > maxLimit", 88 mem: &Memory{Min: 2, Cap: math.MaxUint32, Max: 2}, 89 expectedErr: "capacity 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", 90 }, 91 { 92 name: "max < min", 93 mem: &Memory{Min: 2, Cap: 2, Max: 0, IsMaxEncoded: true}, 94 expectedErr: "min 2 pages (128 Ki) > max 0 pages (0 Ki)", 95 }, 96 { 97 name: "min > limit", 98 mem: &Memory{Min: math.MaxUint32}, 99 expectedErr: "min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", 100 }, 101 { 102 name: "max > limit", 103 mem: &Memory{Max: math.MaxUint32, IsMaxEncoded: true}, 104 expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", 105 }, 106 } 107 108 for _, tt := range tests { 109 tc := tt 110 111 t.Run(tc.name, func(t *testing.T) { 112 err := tc.mem.Validate(MemoryLimitPages) 113 if tc.expectedErr == "" { 114 require.NoError(t, err) 115 } else { 116 require.EqualError(t, err, tc.expectedErr) 117 } 118 }) 119 } 120 } 121 122 func TestModule_allDeclarations(t *testing.T) { 123 tests := []struct { 124 module *Module 125 expectedFunctions []Index 126 expectedGlobals []GlobalType 127 expectedMemory *Memory 128 expectedTables []Table 129 }{ 130 // Functions. 131 { 132 module: &Module{ 133 ImportSection: []Import{{Type: ExternTypeFunc, DescFunc: 10000}}, 134 FunctionSection: []Index{10, 20, 30}, 135 }, 136 expectedFunctions: []Index{10000, 10, 20, 30}, 137 }, 138 { 139 module: &Module{ 140 FunctionSection: []Index{10, 20, 30}, 141 }, 142 expectedFunctions: []Index{10, 20, 30}, 143 }, 144 { 145 module: &Module{ 146 ImportSection: []Import{{Type: ExternTypeFunc, DescFunc: 10000}}, 147 }, 148 expectedFunctions: []Index{10000}, 149 }, 150 // Globals. 151 { 152 module: &Module{ 153 ImportSection: []Import{{Type: ExternTypeGlobal, DescGlobal: GlobalType{Mutable: false}}}, 154 GlobalSection: []Global{{Type: GlobalType{Mutable: true}}}, 155 }, 156 expectedGlobals: []GlobalType{{Mutable: false}, {Mutable: true}}, 157 }, 158 { 159 module: &Module{ 160 GlobalSection: []Global{{Type: GlobalType{Mutable: true}}}, 161 }, 162 expectedGlobals: []GlobalType{{Mutable: true}}, 163 }, 164 { 165 module: &Module{ 166 ImportSection: []Import{{Type: ExternTypeGlobal, DescGlobal: GlobalType{Mutable: false}}}, 167 }, 168 expectedGlobals: []GlobalType{{Mutable: false}}, 169 }, 170 // Memories. 171 { 172 module: &Module{ 173 ImportSection: []Import{{Type: ExternTypeMemory, DescMem: &Memory{Min: 1, Max: 10}}}, 174 }, 175 expectedMemory: &Memory{Min: 1, Max: 10}, 176 }, 177 { 178 module: &Module{ 179 MemorySection: &Memory{Min: 100}, 180 }, 181 expectedMemory: &Memory{Min: 100}, 182 }, 183 // Tables. 184 { 185 module: &Module{ 186 ImportSection: []Import{{Type: ExternTypeTable, DescTable: Table{Min: 1}}}, 187 }, 188 expectedTables: []Table{{Min: 1}}, 189 }, 190 { 191 module: &Module{ 192 TableSection: []Table{{Min: 10}}, 193 }, 194 expectedTables: []Table{{Min: 10}}, 195 }, 196 } 197 198 for i, tt := range tests { 199 tc := tt 200 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 201 functions, globals, memory, tables, err := tc.module.AllDeclarations() 202 require.NoError(t, err) 203 require.Equal(t, tc.expectedFunctions, functions) 204 require.Equal(t, tc.expectedGlobals, globals) 205 require.Equal(t, tc.expectedTables, tables) 206 require.Equal(t, tc.expectedMemory, memory) 207 }) 208 } 209 } 210 211 func TestValidateConstExpression(t *testing.T) { 212 t.Run("invalid opcode", func(t *testing.T) { 213 expr := ConstantExpression{Opcode: OpcodeNop} 214 err := validateConstExpression(nil, 0, &expr, valueTypeUnknown) 215 require.Error(t, err) 216 }) 217 for _, vt := range []ValueType{ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64} { 218 t.Run(ValueTypeName(vt), func(t *testing.T) { 219 t.Run("valid", func(t *testing.T) { 220 expr := ConstantExpression{} 221 switch vt { 222 case ValueTypeI32: 223 expr.Data = []byte{1} 224 expr.Opcode = OpcodeI32Const 225 case ValueTypeI64: 226 expr.Data = []byte{2} 227 expr.Opcode = OpcodeI64Const 228 case ValueTypeF32: 229 expr.Data = u64.LeBytes(api.EncodeF32(math.MaxFloat32)) 230 expr.Opcode = OpcodeF32Const 231 case ValueTypeF64: 232 expr.Data = u64.LeBytes(api.EncodeF64(math.MaxFloat64)) 233 expr.Opcode = OpcodeF64Const 234 } 235 236 err := validateConstExpression(nil, 0, &expr, vt) 237 require.NoError(t, err) 238 }) 239 t.Run("invalid", func(t *testing.T) { 240 // Empty data must be failure. 241 expr := ConstantExpression{Data: make([]byte, 0)} 242 switch vt { 243 case ValueTypeI32: 244 expr.Opcode = OpcodeI32Const 245 case ValueTypeI64: 246 expr.Opcode = OpcodeI64Const 247 case ValueTypeF32: 248 expr.Opcode = OpcodeF32Const 249 case ValueTypeF64: 250 expr.Opcode = OpcodeF64Const 251 } 252 err := validateConstExpression(nil, 0, &expr, vt) 253 require.Error(t, err) 254 }) 255 }) 256 } 257 t.Run("ref types", func(t *testing.T) { 258 t.Run("ref.func", func(t *testing.T) { 259 expr := &ConstantExpression{Data: []byte{5}, Opcode: OpcodeRefFunc} 260 err := validateConstExpression(nil, 10, expr, ValueTypeFuncref) 261 require.NoError(t, err) 262 err = validateConstExpression(nil, 2, expr, ValueTypeFuncref) 263 require.EqualError(t, err, "ref.func index out of range [5] with length 1") 264 }) 265 t.Run("ref.null", func(t *testing.T) { 266 err := validateConstExpression(nil, 0, 267 &ConstantExpression{Data: []byte{ValueTypeFuncref}, Opcode: OpcodeRefNull}, 268 ValueTypeFuncref) 269 require.NoError(t, err) 270 err = validateConstExpression(nil, 0, 271 &ConstantExpression{Data: []byte{ValueTypeExternref}, Opcode: OpcodeRefNull}, 272 ValueTypeExternref) 273 require.NoError(t, err) 274 err = validateConstExpression(nil, 0, 275 &ConstantExpression{Data: []byte{0xff}, Opcode: OpcodeRefNull}, 276 ValueTypeExternref) 277 require.EqualError(t, err, "invalid type for ref.null: 0xff") 278 }) 279 }) 280 t.Run("global expr", func(t *testing.T) { 281 t.Run("failed to read global index", func(t *testing.T) { 282 // Empty data for global index is invalid. 283 expr := &ConstantExpression{Data: make([]byte, 0), Opcode: OpcodeGlobalGet} 284 err := validateConstExpression(nil, 0, expr, valueTypeUnknown) 285 require.Error(t, err) 286 }) 287 t.Run("global index out of range", func(t *testing.T) { 288 // Data holds the index in leb128 and this time the value exceeds len(globals) (=0). 289 expr := &ConstantExpression{Data: []byte{1}, Opcode: OpcodeGlobalGet} 290 var globals []GlobalType 291 err := validateConstExpression(globals, 0, expr, valueTypeUnknown) 292 require.Error(t, err) 293 }) 294 295 t.Run("type mismatch", func(t *testing.T) { 296 for _, vt := range []ValueType{ 297 ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64, 298 } { 299 t.Run(ValueTypeName(vt), func(t *testing.T) { 300 // The index specified in Data equals zero. 301 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet} 302 globals := []GlobalType{{ValType: valueTypeUnknown}} 303 304 err := validateConstExpression(globals, 0, expr, vt) 305 require.Error(t, err) 306 }) 307 } 308 }) 309 t.Run("ok", func(t *testing.T) { 310 for _, vt := range []ValueType{ 311 ValueTypeI32, ValueTypeI64, ValueTypeF32, ValueTypeF64, 312 } { 313 t.Run(ValueTypeName(vt), func(t *testing.T) { 314 // The index specified in Data equals zero. 315 expr := &ConstantExpression{Data: []byte{0}, Opcode: OpcodeGlobalGet} 316 globals := []GlobalType{{ValType: vt}} 317 318 err := validateConstExpression(globals, 0, expr, vt) 319 require.NoError(t, err) 320 }) 321 } 322 }) 323 }) 324 } 325 326 func TestModule_Validate_Errors(t *testing.T) { 327 zero := Index(0) 328 tests := []struct { 329 name string 330 input *Module 331 expectedErr string 332 }{ 333 { 334 name: "StartSection points to an invalid func", 335 input: &Module{ 336 TypeSection: nil, 337 FunctionSection: []uint32{0}, 338 CodeSection: []Code{{Body: []byte{OpcodeEnd}}}, 339 StartSection: &zero, 340 }, 341 expectedErr: "invalid start function: func[0] has an invalid type", 342 }, 343 } 344 345 for _, tt := range tests { 346 tc := tt 347 348 t.Run(tc.name, func(t *testing.T) { 349 err := tc.input.Validate(api.CoreFeaturesV1) 350 require.EqualError(t, err, tc.expectedErr) 351 }) 352 } 353 } 354 355 func TestModule_validateStartSection(t *testing.T) { 356 t.Run("no start section", func(t *testing.T) { 357 m := Module{} 358 err := m.validateStartSection() 359 require.NoError(t, err) 360 }) 361 362 t.Run("invalid type", func(t *testing.T) { 363 for _, ft := range []FunctionType{ 364 {Params: []ValueType{ValueTypeI32}}, 365 {Results: []ValueType{ValueTypeI32}}, 366 {Params: []ValueType{ValueTypeI32}, Results: []ValueType{ValueTypeI32}}, 367 } { 368 t.Run(ft.String(), func(t *testing.T) { 369 index := uint32(0) 370 m := Module{StartSection: &index, FunctionSection: []uint32{0}, TypeSection: []FunctionType{ft}} 371 err := m.validateStartSection() 372 require.Error(t, err) 373 }) 374 } 375 }) 376 t.Run("imported valid func", func(t *testing.T) { 377 index := Index(1) 378 m := Module{ 379 StartSection: &index, 380 TypeSection: []FunctionType{{}, {Results: []ValueType{ValueTypeI32}}}, 381 ImportFunctionCount: 2, 382 ImportSection: []Import{ 383 {Type: ExternTypeFunc, DescFunc: 1}, 384 // import with index 1 is global but this should be skipped when searching imported functions. 385 {Type: ExternTypeGlobal}, 386 {Type: ExternTypeFunc, DescFunc: 0}, // This one must be selected. 387 }, 388 } 389 err := m.validateStartSection() 390 require.NoError(t, err) 391 }) 392 } 393 394 func TestModule_validateGlobals(t *testing.T) { 395 t.Run("too many globals", func(t *testing.T) { 396 m := Module{} 397 err := m.validateGlobals(make([]GlobalType, 10), 0, 9) 398 require.Error(t, err) 399 require.EqualError(t, err, "too many globals in a module") 400 }) 401 t.Run("global index out of range", func(t *testing.T) { 402 m := Module{GlobalSection: []Global{ 403 { 404 Type: GlobalType{ValType: ValueTypeI32}, 405 // Trying to reference globals[1] which is not imported. 406 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}}, 407 }, 408 }} 409 err := m.validateGlobals(nil, 0, 9) 410 require.Error(t, err) 411 require.EqualError(t, err, "global index out of range") 412 }) 413 t.Run("invalid const expression", func(t *testing.T) { 414 m := Module{GlobalSection: []Global{ 415 { 416 Type: GlobalType{ValType: valueTypeUnknown}, 417 Init: ConstantExpression{Opcode: OpcodeUnreachable}, 418 }, 419 }} 420 err := m.validateGlobals(nil, 0, 9) 421 require.Error(t, err) 422 require.EqualError(t, err, "invalid opcode for const expression: 0x0") 423 }) 424 t.Run("ok", func(t *testing.T) { 425 m := Module{GlobalSection: []Global{ 426 { 427 Type: GlobalType{ValType: ValueTypeI32}, 428 Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const0}, 429 }, 430 }} 431 err := m.validateGlobals(nil, 0, 9) 432 require.NoError(t, err) 433 }) 434 t.Run("ok with imported global", func(t *testing.T) { 435 m := Module{ 436 ImportGlobalCount: 1, 437 GlobalSection: []Global{ 438 { 439 Type: GlobalType{ValType: ValueTypeI32}, 440 // Trying to reference globals[1] which is imported. 441 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}}, 442 }, 443 }, 444 ImportSection: []Import{{Type: ExternTypeGlobal}}, 445 } 446 globalDeclarations := []GlobalType{ 447 {ValType: ValueTypeI32}, // Imported one. 448 {}, // the local one trying to validate. 449 } 450 err := m.validateGlobals(globalDeclarations, 0, 9) 451 require.NoError(t, err) 452 }) 453 } 454 455 func TestModule_validateFunctions(t *testing.T) { 456 t.Run("ok", func(t *testing.T) { 457 m := Module{ 458 TypeSection: []FunctionType{v_v}, 459 FunctionSection: []uint32{0}, 460 CodeSection: []Code{{Body: []byte{OpcodeI32Const, 0, OpcodeDrop, OpcodeEnd}}}, 461 } 462 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 463 require.NoError(t, err) 464 }) 465 t.Run("too many functions", func(t *testing.T) { 466 m := Module{} 467 err := m.validateFunctions(api.CoreFeaturesV1, []uint32{1, 2, 3, 4}, nil, nil, nil, 3) 468 require.Error(t, err) 469 require.EqualError(t, err, "too many functions (4) in a module") 470 }) 471 t.Run("function, but no code", func(t *testing.T) { 472 m := Module{ 473 TypeSection: []FunctionType{v_v}, 474 FunctionSection: []Index{0}, 475 CodeSection: nil, 476 } 477 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 478 require.Error(t, err) 479 require.EqualError(t, err, "code count (0) != function count (1)") 480 }) 481 t.Run("function out of range of code", func(t *testing.T) { 482 m := Module{ 483 TypeSection: []FunctionType{v_v}, 484 FunctionSection: []Index{1}, 485 CodeSection: []Code{{Body: []byte{OpcodeEnd}}}, 486 } 487 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 488 require.Error(t, err) 489 require.EqualError(t, err, "invalid function[0]: type section index 1 out of range") 490 }) 491 t.Run("invalid", func(t *testing.T) { 492 m := Module{ 493 TypeSection: []FunctionType{v_v}, 494 FunctionSection: []Index{0}, 495 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}}, 496 } 497 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 498 require.Error(t, err) 499 require.Contains(t, err.Error(), "invalid function[0]: cannot pop the 1st f32 operand") 500 }) 501 t.Run("in- exported", func(t *testing.T) { 502 m := Module{ 503 TypeSection: []FunctionType{v_v}, 504 FunctionSection: []Index{0}, 505 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}}, 506 ExportSection: []Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}}, 507 } 508 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 509 require.Error(t, err) 510 require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`) 511 }) 512 t.Run("in- exported after import", func(t *testing.T) { 513 m := Module{ 514 ImportFunctionCount: 1, 515 TypeSection: []FunctionType{v_v}, 516 ImportSection: []Import{{Type: ExternTypeFunc}}, 517 FunctionSection: []Index{0}, 518 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}}, 519 ExportSection: []Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}}, 520 } 521 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 522 require.Error(t, err) 523 require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`) 524 }) 525 t.Run("in- exported twice", func(t *testing.T) { 526 m := Module{ 527 TypeSection: []FunctionType{v_v}, 528 FunctionSection: []Index{0}, 529 CodeSection: []Code{{Body: []byte{OpcodeF32Abs}}}, 530 ExportSection: []Export{ 531 {Name: "f1", Type: ExternTypeFunc, Index: 0}, 532 {Name: "f2", Type: ExternTypeFunc, Index: 0}, 533 }, 534 } 535 err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex) 536 require.Error(t, err) 537 require.Contains(t, err.Error(), `invalid function[0] export["f1","f2"]: cannot pop the 1st f32`) 538 }) 539 } 540 541 func TestModule_validateMemory(t *testing.T) { 542 t.Run("active data segment exits but memory not declared", func(t *testing.T) { 543 m := Module{DataSection: []DataSegment{{OffsetExpression: ConstantExpression{}}}} 544 err := m.validateMemory(nil, nil, api.CoreFeaturesV1) 545 require.Error(t, err) 546 require.Contains(t, "unknown memory", err.Error()) 547 }) 548 t.Run("invalid const expr", func(t *testing.T) { 549 m := Module{DataSection: []DataSegment{{ 550 OffsetExpression: ConstantExpression{ 551 Opcode: OpcodeUnreachable, // Invalid! 552 }, 553 }}} 554 err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1) 555 require.EqualError(t, err, "calculate offset: invalid opcode for const expression: 0x0") 556 }) 557 t.Run("ok", func(t *testing.T) { 558 m := Module{DataSection: []DataSegment{{ 559 Init: []byte{0x1}, 560 OffsetExpression: ConstantExpression{ 561 Opcode: OpcodeI32Const, 562 Data: leb128.EncodeInt32(1), 563 }, 564 }}} 565 err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1) 566 require.NoError(t, err) 567 }) 568 } 569 570 func TestModule_validateImports(t *testing.T) { 571 tests := []struct { 572 name string 573 enabledFeatures api.CoreFeatures 574 i *Import 575 expectedErr string 576 }{ 577 {name: "empty import section"}, 578 { 579 name: "reject empty named module", 580 enabledFeatures: api.CoreFeaturesV1, 581 i: &Import{Module: "", Name: "n", Type: ExternTypeFunc, DescFunc: 0}, 582 expectedErr: "import[0] has an empty module name", 583 }, 584 { 585 name: "func", 586 enabledFeatures: api.CoreFeaturesV1, 587 i: &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0}, 588 }, 589 { 590 name: "func type index out of range ", 591 enabledFeatures: api.CoreFeaturesV1, 592 i: &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 100}, 593 expectedErr: "invalid import[\"m\".\"n\"] function: type index out of range", 594 }, 595 { 596 name: "global var disabled", 597 enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false), 598 i: &Import{ 599 Module: "m", 600 Name: "n", 601 Type: ExternTypeGlobal, 602 DescGlobal: GlobalType{ValType: ValueTypeI32, Mutable: true}, 603 }, 604 expectedErr: `invalid import["m"."n"] global: feature "mutable-global" is disabled`, 605 }, 606 { 607 name: "table", 608 enabledFeatures: api.CoreFeaturesV1, 609 i: &Import{ 610 Module: "m", 611 Name: "n", 612 Type: ExternTypeTable, 613 DescTable: Table{Min: 1}, 614 }, 615 }, 616 { 617 name: "memory", 618 enabledFeatures: api.CoreFeaturesV1, 619 i: &Import{ 620 Module: "m", 621 Name: "n", 622 Type: ExternTypeMemory, 623 DescMem: &Memory{Min: 1}, 624 }, 625 }, 626 } 627 628 for _, tt := range tests { 629 tc := tt 630 t.Run(tc.name, func(t *testing.T) { 631 m := Module{TypeSection: []FunctionType{{}}} 632 if tc.i != nil { 633 m.ImportSection = []Import{*tc.i} 634 } 635 err := m.validateImports(tc.enabledFeatures) 636 if tc.expectedErr != "" { 637 require.EqualError(t, err, tc.expectedErr) 638 } else { 639 require.NoError(t, err) 640 } 641 }) 642 } 643 } 644 645 func TestModule_validateExports(t *testing.T) { 646 tests := []struct { 647 name string 648 enabledFeatures api.CoreFeatures 649 exportSection []Export 650 functions []Index 651 globals []GlobalType 652 memory *Memory 653 tables []Table 654 expectedErr string 655 }{ 656 {name: "empty export section", exportSection: []Export{}}, 657 { 658 name: "func", 659 enabledFeatures: api.CoreFeaturesV1, 660 exportSection: []Export{{Type: ExternTypeFunc, Index: 0}}, 661 functions: []Index{100 /* arbitrary type id*/}, 662 }, 663 { 664 name: "func out of range", 665 enabledFeatures: api.CoreFeaturesV1, 666 exportSection: []Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}}, 667 functions: []Index{100 /* arbitrary type id*/}, 668 expectedErr: `unknown function for export["e"]`, 669 }, 670 { 671 name: "global const", 672 enabledFeatures: api.CoreFeaturesV1, 673 exportSection: []Export{{Type: ExternTypeGlobal, Index: 0}}, 674 globals: []GlobalType{{ValType: ValueTypeI32}}, 675 }, 676 { 677 name: "global var", 678 enabledFeatures: api.CoreFeaturesV1, 679 exportSection: []Export{{Type: ExternTypeGlobal, Index: 0}}, 680 globals: []GlobalType{{ValType: ValueTypeI32, Mutable: true}}, 681 }, 682 { 683 name: "global var disabled", 684 enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false), 685 exportSection: []Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}}, 686 globals: []GlobalType{{ValType: ValueTypeI32, Mutable: true}}, 687 expectedErr: `invalid export["e"] global[0]: feature "mutable-global" is disabled`, 688 }, 689 { 690 name: "global out of range", 691 enabledFeatures: api.CoreFeaturesV1, 692 exportSection: []Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}}, 693 globals: []GlobalType{{}}, 694 expectedErr: `unknown global for export["e"]`, 695 }, 696 { 697 name: "table", 698 enabledFeatures: api.CoreFeaturesV1, 699 exportSection: []Export{{Type: ExternTypeTable, Index: 0}}, 700 tables: []Table{{}}, 701 }, 702 { 703 name: "multiple tables", 704 enabledFeatures: api.CoreFeaturesV1, 705 exportSection: []Export{{Type: ExternTypeTable, Index: 0}, {Type: ExternTypeTable, Index: 1}, {Type: ExternTypeTable, Index: 2}}, 706 tables: []Table{{}, {}, {}}, 707 }, 708 { 709 name: "table out of range", 710 enabledFeatures: api.CoreFeaturesV1, 711 exportSection: []Export{{Type: ExternTypeTable, Index: 1, Name: "e"}}, 712 tables: []Table{}, 713 expectedErr: `table for export["e"] out of range`, 714 }, 715 { 716 name: "memory", 717 enabledFeatures: api.CoreFeaturesV1, 718 exportSection: []Export{{Type: ExternTypeMemory, Index: 0}}, 719 memory: &Memory{}, 720 }, 721 { 722 name: "memory out of range", 723 enabledFeatures: api.CoreFeaturesV1, 724 exportSection: []Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}}, 725 tables: []Table{}, 726 expectedErr: `memory for export["e"] out of range`, 727 }, 728 } 729 730 for _, tt := range tests { 731 tc := tt 732 t.Run(tc.name, func(t *testing.T) { 733 m := Module{ExportSection: tc.exportSection} 734 err := m.validateExports(tc.enabledFeatures, tc.functions, tc.globals, tc.memory, tc.tables) 735 if tc.expectedErr != "" { 736 require.EqualError(t, err, tc.expectedErr) 737 } else { 738 require.NoError(t, err) 739 } 740 }) 741 } 742 } 743 744 func TestModule_buildGlobals(t *testing.T) { 745 const localFuncRefInstructionIndex = uint32(0xffff) 746 747 minusOne := int32(-1) 748 m := &Module{ 749 ImportGlobalCount: 2, 750 GlobalSection: []Global{ 751 { 752 Type: GlobalType{Mutable: true, ValType: ValueTypeF64}, 753 Init: ConstantExpression{ 754 Opcode: OpcodeF64Const, 755 Data: u64.LeBytes(api.EncodeF64(math.MaxFloat64)), 756 }, 757 }, 758 { 759 Type: GlobalType{Mutable: false, ValType: ValueTypeI32}, 760 Init: ConstantExpression{ 761 Opcode: OpcodeI32Const, 762 Data: leb128.EncodeInt32(math.MaxInt32), 763 }, 764 }, 765 { 766 Type: GlobalType{Mutable: false, ValType: ValueTypeI32}, 767 Init: ConstantExpression{ 768 Opcode: OpcodeI32Const, 769 Data: leb128.EncodeInt32(minusOne), 770 }, 771 }, 772 { 773 Type: GlobalType{Mutable: false, ValType: ValueTypeV128}, 774 Init: ConstantExpression{ 775 Opcode: OpcodeVecV128Const, 776 Data: []byte{ 777 1, 0, 0, 0, 0, 0, 0, 0, 778 2, 0, 0, 0, 0, 0, 0, 0, 779 }, 780 }, 781 }, 782 { 783 Type: GlobalType{Mutable: false, ValType: ValueTypeExternref}, 784 Init: ConstantExpression{Opcode: OpcodeRefNull, Data: []byte{ValueTypeExternref}}, 785 }, 786 { 787 Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref}, 788 Init: ConstantExpression{Opcode: OpcodeRefNull, Data: []byte{ValueTypeFuncref}}, 789 }, 790 { 791 Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref}, 792 Init: ConstantExpression{Opcode: OpcodeRefFunc, Data: leb128.EncodeUint32(localFuncRefInstructionIndex)}, 793 }, 794 { 795 Type: GlobalType{Mutable: false, ValType: ValueTypeExternref}, 796 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{0}}, 797 }, 798 { 799 Type: GlobalType{Mutable: false, ValType: ValueTypeFuncref}, 800 Init: ConstantExpression{Opcode: OpcodeGlobalGet, Data: []byte{1}}, 801 }, 802 }, 803 } 804 805 imported := []*GlobalInstance{ 806 {Type: GlobalType{ValType: ValueTypeExternref}, Val: 0x54321}, 807 {Type: GlobalType{ValType: ValueTypeFuncref}, Val: 0x12345}, 808 } 809 810 mi := &ModuleInstance{ 811 Globals: make([]*GlobalInstance, m.ImportGlobalCount+uint32(len(m.GlobalSection))), 812 } 813 814 mi.Globals[0], mi.Globals[1] = imported[0], imported[1] 815 816 mi.buildGlobals(m, func(funcIndex Index) Reference { 817 require.Equal(t, localFuncRefInstructionIndex, funcIndex) 818 return 0x99999 819 }) 820 expectedGlobals := []*GlobalInstance{ 821 imported[0], imported[1], 822 {Type: GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(math.MaxFloat64)}, 823 {Type: GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(int32(math.MaxInt32))}, 824 // Higher bits are must be zeroed for i32 globals, not signed-extended. See #656. 825 {Type: GlobalType{ValType: ValueTypeI32, Mutable: false}, Val: uint64(uint32(minusOne))}, 826 {Type: GlobalType{ValType: ValueTypeV128, Mutable: false}, Val: 0x1, ValHi: 0x2}, 827 {Type: GlobalType{ValType: ValueTypeExternref, Mutable: false}, Val: 0}, 828 {Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0}, 829 {Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0x99999}, 830 {Type: GlobalType{ValType: ValueTypeExternref, Mutable: false}, Val: 0x54321}, 831 {Type: GlobalType{ValType: ValueTypeFuncref, Mutable: false}, Val: 0x12345}, 832 } 833 require.Equal(t, expectedGlobals, mi.Globals) 834 } 835 836 func TestModule_buildMemoryInstance(t *testing.T) { 837 t.Run("nil", func(t *testing.T) { 838 m := ModuleInstance{} 839 m.buildMemory(&Module{}) 840 require.Nil(t, m.MemoryInstance) 841 }) 842 t.Run("non-nil", func(t *testing.T) { 843 min := uint32(1) 844 max := uint32(10) 845 mDef := MemoryDefinition{moduleName: "foo"} 846 m := ModuleInstance{} 847 m.buildMemory(&Module{ 848 MemorySection: &Memory{Min: min, Cap: min, Max: max}, 849 MemoryDefinitionSection: []MemoryDefinition{mDef}, 850 }) 851 mem := m.MemoryInstance 852 require.Equal(t, min, mem.Min) 853 require.Equal(t, max, mem.Max) 854 require.Equal(t, &mDef, mem.definition) 855 }) 856 } 857 858 func TestModule_validateDataCountSection(t *testing.T) { 859 t.Run("ok", func(t *testing.T) { 860 for _, m := range []*Module{ 861 { 862 DataSection: []DataSegment{}, 863 DataCountSection: nil, 864 }, 865 { 866 DataSection: []DataSegment{{}, {}}, 867 DataCountSection: nil, 868 }, 869 } { 870 err := m.validateDataCountSection() 871 require.NoError(t, err) 872 } 873 }) 874 t.Run("error", func(t *testing.T) { 875 count := uint32(1) 876 for _, m := range []*Module{ 877 { 878 DataSection: []DataSegment{}, 879 DataCountSection: &count, 880 }, 881 { 882 DataSection: []DataSegment{{}, {}}, 883 DataCountSection: &count, 884 }, 885 } { 886 err := m.validateDataCountSection() 887 require.Error(t, err) 888 } 889 }) 890 } 891 892 func TestModule_declaredFunctionIndexes(t *testing.T) { 893 tests := []struct { 894 name string 895 mod *Module 896 exp map[Index]struct{} 897 expErr string 898 }{ 899 { 900 name: "empty", 901 mod: &Module{}, 902 exp: map[uint32]struct{}{}, 903 }, 904 { 905 name: "global", 906 mod: &Module{ 907 ExportSection: []Export{ 908 {Index: 10, Type: ExternTypeFunc}, 909 {Index: 1000, Type: ExternTypeGlobal}, 910 }, 911 }, 912 exp: map[uint32]struct{}{10: {}}, 913 }, 914 { 915 name: "export", 916 mod: &Module{ 917 ExportSection: []Export{ 918 {Index: 1000, Type: ExternTypeGlobal}, 919 {Index: 10, Type: ExternTypeFunc}, 920 }, 921 }, 922 exp: map[uint32]struct{}{10: {}}, 923 }, 924 { 925 name: "element", 926 mod: &Module{ 927 ElementSection: []ElementSegment{ 928 { 929 Mode: ElementModeActive, 930 Init: []Index{0, ElementInitNullReference, 5}, 931 }, 932 { 933 Mode: ElementModeDeclarative, 934 Init: []Index{1, ElementInitNullReference, 5}, 935 }, 936 { 937 Mode: ElementModePassive, 938 Init: []Index{5, 2, ElementInitNullReference, ElementInitNullReference}, 939 }, 940 }, 941 }, 942 exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}}, 943 }, 944 { 945 name: "all", 946 mod: &Module{ 947 ExportSection: []Export{ 948 {Index: 10, Type: ExternTypeGlobal}, 949 {Index: 1000, Type: ExternTypeFunc}, 950 }, 951 GlobalSection: []Global{ 952 { 953 Init: ConstantExpression{ 954 Opcode: OpcodeI32Const, // not funcref. 955 Data: leb128.EncodeInt32(-1), 956 }, 957 }, 958 { 959 Init: ConstantExpression{ 960 Opcode: OpcodeRefFunc, 961 Data: leb128.EncodeInt32(123), 962 }, 963 }, 964 }, 965 ElementSection: []ElementSegment{ 966 { 967 Mode: ElementModeActive, 968 Init: []Index{0, ElementInitNullReference, 5}, 969 }, 970 { 971 Mode: ElementModeDeclarative, 972 Init: []Index{1, ElementInitNullReference, 5}, 973 }, 974 { 975 Mode: ElementModePassive, 976 Init: []Index{5, 2, ElementInitNullReference, ElementInitNullReference}, 977 }, 978 }, 979 }, 980 exp: map[uint32]struct{}{0: {}, 1: {}, 2: {}, 5: {}, 123: {}, 1000: {}}, 981 }, 982 { 983 mod: &Module{ 984 GlobalSection: []Global{ 985 { 986 Init: ConstantExpression{ 987 Opcode: OpcodeRefFunc, 988 Data: nil, 989 }, 990 }, 991 }, 992 }, 993 name: "invalid global", 994 expErr: `global[0] failed to initialize: EOF`, 995 }, 996 } 997 998 for _, tt := range tests { 999 tc := tt 1000 t.Run(tc.name, func(t *testing.T) { 1001 actual, err := tc.mod.declaredFunctionIndexes() 1002 if tc.expErr != "" { 1003 require.EqualError(t, err, tc.expErr) 1004 } else { 1005 require.NoError(t, err) 1006 require.Equal(t, tc.exp, actual) 1007 } 1008 }) 1009 } 1010 } 1011 1012 func TestModule_AssignModuleID(t *testing.T) { 1013 getID := func(bin []byte, withListener, withEnsureTermination bool) ModuleID { 1014 m := Module{} 1015 m.AssignModuleID(bin, withListener, withEnsureTermination) 1016 return m.ID 1017 } 1018 1019 // Ensures that different args always produce the different IDs. 1020 exists := map[ModuleID]struct{}{} 1021 for _, tc := range []struct { 1022 bin []byte 1023 withListener, withEnsureTermination bool 1024 }{ 1025 {bin: []byte{1, 2, 3}, withListener: false, withEnsureTermination: false}, 1026 {bin: []byte{1, 2, 3}, withListener: false, withEnsureTermination: true}, 1027 {bin: []byte{1, 2, 3}, withListener: true, withEnsureTermination: false}, 1028 {bin: []byte{1, 2, 3}, withListener: true, withEnsureTermination: true}, 1029 {bin: []byte{1, 2, 3, 4}, withListener: false, withEnsureTermination: false}, 1030 {bin: []byte{1, 2, 3, 4}, withListener: false, withEnsureTermination: true}, 1031 {bin: []byte{1, 2, 3, 4}, withListener: true, withEnsureTermination: false}, 1032 {bin: []byte{1, 2, 3, 4}, withListener: true, withEnsureTermination: true}, 1033 } { 1034 id := getID(tc.bin, tc.withListener, tc.withEnsureTermination) 1035 _, exist := exists[id] 1036 require.False(t, exist) 1037 exists[id] = struct{}{} 1038 } 1039 }