wa-lang.org/wazero@v1.0.2/internal/wasm/binary/decoder.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 9 "wa-lang.org/wazero/api" 10 "wa-lang.org/wazero/internal/leb128" 11 "wa-lang.org/wazero/internal/wasm" 12 ) 13 14 // DecodeModule implements wasm.DecodeModule for the WebAssembly 1.0 (20191205) Binary Format 15 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-format%E2%91%A0 16 func DecodeModule( 17 binary []byte, 18 enabledFeatures api.CoreFeatures, 19 memoryLimitPages uint32, 20 memoryCapacityFromMax bool, 21 ) (*wasm.Module, error) { 22 r := bytes.NewReader(binary) 23 24 // Magic number. 25 buf := make([]byte, 4) 26 if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, Magic) { 27 return nil, ErrInvalidMagicNumber 28 } 29 30 // Version. 31 if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, version) { 32 return nil, ErrInvalidVersion 33 } 34 35 memorySizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax) 36 37 m := &wasm.Module{} 38 for { 39 // TODO: except custom sections, all others are required to be in order, but we aren't checking yet. 40 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A0%E2%93%AA 41 sectionID, err := r.ReadByte() 42 if err == io.EOF { 43 break 44 } else if err != nil { 45 return nil, fmt.Errorf("read section id: %w", err) 46 } 47 48 sectionSize, _, err := leb128.DecodeUint32(r) 49 if err != nil { 50 return nil, fmt.Errorf("get size of section %s: %v", wasm.SectionIDName(sectionID), err) 51 } 52 53 sectionContentStart := r.Len() 54 switch sectionID { 55 case wasm.SectionIDCustom: 56 // First, validate the section and determine if the section for this name has already been set 57 name, nameSize, decodeErr := decodeUTF8(r, "custom section name") 58 if decodeErr != nil { 59 err = decodeErr 60 break 61 } else if sectionSize < nameSize { 62 err = fmt.Errorf("malformed custom section %s", name) 63 break 64 } else if name == "name" && m.NameSection != nil { 65 err = fmt.Errorf("redundant custom section %s", name) 66 break 67 } 68 69 // Now, either decode the NameSection or skip an unsupported one 70 limit := sectionSize - nameSize 71 if name == "name" { 72 m.NameSection, err = decodeNameSection(r, uint64(limit)) 73 } else { 74 // Note: Not Seek because it doesn't err when given an offset past EOF. Rather, it leads to undefined state. 75 if _, err = io.CopyN(io.Discard, r, int64(limit)); err != nil { 76 return nil, fmt.Errorf("failed to skip name[%s]: %w", name, err) 77 } 78 } 79 80 case wasm.SectionIDType: 81 m.TypeSection, err = decodeTypeSection(enabledFeatures, r) 82 case wasm.SectionIDImport: 83 if m.ImportSection, err = decodeImportSection(r, memorySizer, memoryLimitPages, enabledFeatures); err != nil { 84 return nil, err // avoid re-wrapping the error. 85 } 86 case wasm.SectionIDFunction: 87 m.FunctionSection, err = decodeFunctionSection(r) 88 case wasm.SectionIDTable: 89 m.TableSection, err = decodeTableSection(r, enabledFeatures) 90 case wasm.SectionIDMemory: 91 m.MemorySection, err = decodeMemorySection(r, memorySizer, memoryLimitPages) 92 case wasm.SectionIDGlobal: 93 if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil { 94 return nil, err // avoid re-wrapping the error. 95 } 96 case wasm.SectionIDExport: 97 m.ExportSection, err = decodeExportSection(r) 98 case wasm.SectionIDStart: 99 if m.StartSection != nil { 100 return nil, errors.New("multiple start sections are invalid") 101 } 102 m.StartSection, err = decodeStartSection(r) 103 case wasm.SectionIDElement: 104 m.ElementSection, err = decodeElementSection(r, enabledFeatures) 105 case wasm.SectionIDCode: 106 m.CodeSection, err = decodeCodeSection(r) 107 case wasm.SectionIDData: 108 m.DataSection, err = decodeDataSection(r, enabledFeatures) 109 case wasm.SectionIDDataCount: 110 if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil { 111 return nil, fmt.Errorf("data count section not supported as %v", err) 112 } 113 m.DataCountSection, err = decodeDataCountSection(r) 114 default: 115 err = ErrInvalidSectionID 116 } 117 118 readBytes := sectionContentStart - r.Len() 119 if err == nil && int(sectionSize) != readBytes { 120 err = fmt.Errorf("invalid section length: expected to be %d but got %d", sectionSize, readBytes) 121 } 122 123 if err != nil { 124 return nil, fmt.Errorf("section %s: %v", wasm.SectionIDName(sectionID), err) 125 } 126 } 127 128 functionCount, codeCount := m.SectionElementCount(wasm.SectionIDFunction), m.SectionElementCount(wasm.SectionIDCode) 129 if functionCount != codeCount { 130 return nil, fmt.Errorf("function and code section have inconsistent lengths: %d != %d", functionCount, codeCount) 131 } 132 return m, nil 133 } 134 135 // memorySizer derives min, capacity and max pages from decoded wasm. 136 type memorySizer func(minPages uint32, maxPages *uint32) (min uint32, capacity uint32, max uint32) 137 138 // newMemorySizer sets capacity to minPages unless max is defined and 139 // memoryCapacityFromMax is true. 140 func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memorySizer { 141 return func(minPages uint32, maxPages *uint32) (min, capacity, max uint32) { 142 if maxPages != nil { 143 if memoryCapacityFromMax { 144 return minPages, *maxPages, *maxPages 145 } 146 return minPages, minPages, *maxPages 147 } 148 return minPages, minPages, memoryLimitPages 149 } 150 }