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