decoder.go

  1package binary
  2
  3import (
  4	"bytes"
  5	"debug/dwarf"
  6	"errors"
  7	"fmt"
  8	"io"
  9
 10	"github.com/tetratelabs/wazero/api"
 11	"github.com/tetratelabs/wazero/internal/leb128"
 12	"github.com/tetratelabs/wazero/internal/wasm"
 13	"github.com/tetratelabs/wazero/internal/wasmdebug"
 14)
 15
 16// DecodeModule implements wasm.DecodeModule for the WebAssembly 1.0 (20191205) Binary Format
 17// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-format%E2%91%A0
 18func DecodeModule(
 19	binary []byte,
 20	enabledFeatures api.CoreFeatures,
 21	memoryLimitPages uint32,
 22	memoryCapacityFromMax,
 23	dwarfEnabled, storeCustomSections bool,
 24) (*wasm.Module, error) {
 25	r := bytes.NewReader(binary)
 26
 27	// Magic number.
 28	buf := make([]byte, 4)
 29	if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, Magic) {
 30		return nil, ErrInvalidMagicNumber
 31	}
 32
 33	// Version.
 34	if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, version) {
 35		return nil, ErrInvalidVersion
 36	}
 37
 38	memSizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax)
 39
 40	m := &wasm.Module{}
 41	var info, line, str, abbrev, ranges []byte // For DWARF Data.
 42	for {
 43		// TODO: except custom sections, all others are required to be in order, but we aren't checking yet.
 44		// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A0%E2%93%AA
 45		sectionID, err := r.ReadByte()
 46		if err == io.EOF {
 47			break
 48		} else if err != nil {
 49			return nil, fmt.Errorf("read section id: %w", err)
 50		}
 51
 52		sectionSize, _, err := leb128.DecodeUint32(r)
 53		if err != nil {
 54			return nil, fmt.Errorf("get size of section %s: %v", wasm.SectionIDName(sectionID), err)
 55		}
 56
 57		sectionContentStart := r.Len()
 58		switch sectionID {
 59		case wasm.SectionIDCustom:
 60			// First, validate the section and determine if the section for this name has already been set
 61			name, nameSize, decodeErr := decodeUTF8(r, "custom section name")
 62			if decodeErr != nil {
 63				err = decodeErr
 64				break
 65			} else if sectionSize < nameSize {
 66				err = fmt.Errorf("malformed custom section %s", name)
 67				break
 68			} else if name == "name" && m.NameSection != nil {
 69				err = fmt.Errorf("redundant custom section %s", name)
 70				break
 71			}
 72
 73			// Now, either decode the NameSection or CustomSection
 74			limit := sectionSize - nameSize
 75
 76			var c *wasm.CustomSection
 77			if name != "name" {
 78				if storeCustomSections || dwarfEnabled {
 79					c, err = decodeCustomSection(r, name, uint64(limit))
 80					if err != nil {
 81						return nil, fmt.Errorf("failed to read custom section name[%s]: %w", name, err)
 82					}
 83					m.CustomSections = append(m.CustomSections, c)
 84					if dwarfEnabled {
 85						switch name {
 86						case ".debug_info":
 87							info = c.Data
 88						case ".debug_line":
 89							line = c.Data
 90						case ".debug_str":
 91							str = c.Data
 92						case ".debug_abbrev":
 93							abbrev = c.Data
 94						case ".debug_ranges":
 95							ranges = c.Data
 96						}
 97					}
 98				} else {
 99					if _, err = io.CopyN(io.Discard, r, int64(limit)); err != nil {
100						return nil, fmt.Errorf("failed to skip name[%s]: %w", name, err)
101					}
102				}
103			} else {
104				m.NameSection, err = decodeNameSection(r, uint64(limit))
105			}
106		case wasm.SectionIDType:
107			m.TypeSection, err = decodeTypeSection(enabledFeatures, r)
108		case wasm.SectionIDImport:
109			m.ImportSection, m.ImportPerModule, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memSizer, memoryLimitPages, enabledFeatures)
110			if err != nil {
111				return nil, err // avoid re-wrapping the error.
112			}
113		case wasm.SectionIDFunction:
114			m.FunctionSection, err = decodeFunctionSection(r)
115		case wasm.SectionIDTable:
116			m.TableSection, err = decodeTableSection(r, enabledFeatures)
117		case wasm.SectionIDMemory:
118			m.MemorySection, err = decodeMemorySection(r, enabledFeatures, memSizer, memoryLimitPages)
119		case wasm.SectionIDGlobal:
120			if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil {
121				return nil, err // avoid re-wrapping the error.
122			}
123		case wasm.SectionIDExport:
124			m.ExportSection, m.Exports, err = decodeExportSection(r)
125		case wasm.SectionIDStart:
126			if m.StartSection != nil {
127				return nil, errors.New("multiple start sections are invalid")
128			}
129			m.StartSection, err = decodeStartSection(r)
130		case wasm.SectionIDElement:
131			m.ElementSection, err = decodeElementSection(r, enabledFeatures)
132		case wasm.SectionIDCode:
133			m.CodeSection, err = decodeCodeSection(r)
134		case wasm.SectionIDData:
135			m.DataSection, err = decodeDataSection(r, enabledFeatures)
136		case wasm.SectionIDDataCount:
137			if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
138				return nil, fmt.Errorf("data count section not supported as %v", err)
139			}
140			m.DataCountSection, err = decodeDataCountSection(r)
141		default:
142			err = ErrInvalidSectionID
143		}
144
145		readBytes := sectionContentStart - r.Len()
146		if err == nil && int(sectionSize) != readBytes {
147			err = fmt.Errorf("invalid section length: expected to be %d but got %d", sectionSize, readBytes)
148		}
149
150		if err != nil {
151			return nil, fmt.Errorf("section %s: %v", wasm.SectionIDName(sectionID), err)
152		}
153	}
154
155	if dwarfEnabled {
156		d, _ := dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
157		m.DWARFLines = wasmdebug.NewDWARFLines(d)
158	}
159
160	functionCount, codeCount := m.SectionElementCount(wasm.SectionIDFunction), m.SectionElementCount(wasm.SectionIDCode)
161	if functionCount != codeCount {
162		return nil, fmt.Errorf("function and code section have inconsistent lengths: %d != %d", functionCount, codeCount)
163	}
164	return m, nil
165}
166
167// memorySizer derives min, capacity and max pages from decoded wasm.
168type memorySizer func(minPages uint32, maxPages *uint32) (min uint32, capacity uint32, max uint32)
169
170// newMemorySizer sets capacity to minPages unless max is defined and
171// memoryCapacityFromMax is true.
172func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memorySizer {
173	return func(minPages uint32, maxPages *uint32) (min, capacity, max uint32) {
174		if maxPages != nil {
175			if memoryCapacityFromMax {
176				return minPages, *maxPages, *maxPages
177			}
178			// This is an invalid value: let it propagate, we will fail later.
179			if *maxPages > wasm.MemoryLimitPages {
180				return minPages, minPages, *maxPages
181			}
182			// This is a valid value, but it goes over the run-time limit: return the limit.
183			if *maxPages > memoryLimitPages {
184				return minPages, minPages, memoryLimitPages
185			}
186			return minPages, minPages, *maxPages
187		}
188		if memoryCapacityFromMax {
189			return minPages, memoryLimitPages, memoryLimitPages
190		}
191		return minPages, minPages, memoryLimitPages
192	}
193}