github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/wasm/binary/decoder.go (about)

     1  package binary
     2  
     3  import (
     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
    18  func 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.
   168  type 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.
   172  func 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  }