wasm_validate (wasm v0.1.0)

View Source

Module-level validation. This is what decides whether you get {error, #{class := invalid}} back, and everything it proves is what the interpreter is then allowed to assume.

It runs as a separate pass over the decoded AST, before any IR is built and never during execution. That separation is the whole point: once a module validates, the interpreter can assume every index is in range, every operand has the type it expects, and every branch target exists. Anything the interpreter would otherwise have to re-check on the hot path is checked exactly once here.

Function bodies are checked by wasm_validate_code. This module owns the surrounding structure: index spaces, limits, imports, exports, segments, start function, and constant expressions.

Summary

Functions

As context/1, memoised per module in the calling process.

Types

annotated()

-type annotated() ::
          {Height :: non_neg_integer(), instr()} |
          {Height :: non_neg_integer(), Base :: non_neg_integer(), instr()}.

externtype()

-type externtype() ::
          {func, typeidx()} |
          {table,
           #tabletype{limits ::
                          #limits{min :: non_neg_integer(),
                                  max :: undefined | non_neg_integer(),
                                  shared :: boolean(),
                                  index_type :: i32 | i64},
                      elemtype :: reftype(),
                      init :: undefined | [instr()]}} |
          {mem,
           #memtype{limits ::
                        #limits{min :: non_neg_integer(),
                                max :: undefined | non_neg_integer(),
                                shared :: boolean(),
                                index_type :: i32 | i64}}} |
          {global, #globaltype{valtype :: valtype(), mut :: mut()}} |
          {tag, #tagtype{type :: typeidx()}}.

funcidx()

-type funcidx() :: non_neg_integer().

heaptype()

-type heaptype() ::
          func | extern | exn | any | eq | i31 | struct | array | nofunc | noextern | noexn | none |
          {type, typeidx()}.

instr()

-type instr() :: atom() | tuple().

memidx()

-type memidx() :: non_neg_integer().

mut()

-type mut() :: const | var.

numtype()

-type numtype() :: i32 | i64 | f32 | f64.

reftype()

-type reftype() :: {ref, null | nonull, heaptype()}.

tableidx()

-type tableidx() :: non_neg_integer().

typeidx()

-type typeidx() :: non_neg_integer().

valtype()

-type valtype() :: numtype() | vectype() | reftype().

vectype()

-type vectype() :: v128.

Functions

cached_context(M)

-spec cached_context(#module{identity :: undefined | {sha256, binary()} | reference(),
                             types ::
                                 [#subtype{final :: boolean(),
                                           supers :: [typeidx()],
                                           body ::
                                               #functype{params :: [valtype()], results :: [valtype()]} |
                                               #structtype{fields ::
                                                               [#fieldtype{type :: valtype() | i8 | i16,
                                                                           mut :: mut()}]} |
                                               #arraytype{field ::
                                                              #fieldtype{type :: valtype() | i8 | i16,
                                                                         mut :: mut()}}}],
                             rec_groups :: [{non_neg_integer(), non_neg_integer()}],
                             imports ::
                                 [#import{module :: binary(), name :: binary(), desc :: externtype()}],
                             funcs ::
                                 [#func{type :: typeidx(),
                                        locals :: [valtype()],
                                        body :: [instr()] | {validated, [annotated()]}}],
                             tables ::
                                 [#tabletype{limits ::
                                                 #limits{min :: non_neg_integer(),
                                                         max :: undefined | non_neg_integer(),
                                                         shared :: boolean(),
                                                         index_type :: i32 | i64},
                                             elemtype :: reftype(),
                                             init :: undefined | [instr()]}],
                             mems ::
                                 [#memtype{limits ::
                                               #limits{min :: non_neg_integer(),
                                                       max :: undefined | non_neg_integer(),
                                                       shared :: boolean(),
                                                       index_type :: i32 | i64}}],
                             tags :: [#tagtype{type :: typeidx()}],
                             globals ::
                                 [#global{type :: #globaltype{valtype :: valtype(), mut :: mut()},
                                          init :: [instr()]}],
                             exports ::
                                 [#export{name :: binary(),
                                          desc :: {func | table | mem | global | tag, non_neg_integer()}}],
                             start :: undefined | funcidx(),
                             elems ::
                                 [#elem{type :: reftype(),
                                        init :: [[instr()]],
                                        mode :: passive | declarative | {active, tableidx(), [instr()]}}],
                             datas ::
                                 [#data{init :: binary(),
                                        mode :: passive | {active, memidx(), [instr()]}}],
                             data_count :: undefined | non_neg_integer(),
                             customs :: [{binary(), binary()}]}) ->
                        #ctx{types :: tuple(),
                             canon :: tuple(),
                             fields :: tuple(),
                             kinds :: tuple(),
                             supers :: tuple(),
                             funcs :: tuple(),
                             tables :: tuple(),
                             mems :: tuple(),
                             globals :: tuple(),
                             tags :: tuple(),
                             elems :: tuple(),
                             n_datas :: non_neg_integer(),
                             n_imported_globals :: non_neg_integer(),
                             refs :: #{non_neg_integer() => true},
                             shared_globals :: #{non_neg_integer() => true},
                             shared_mems :: #{non_neg_integer() => true},
                             locals :: tuple(),
                             results :: [valtype()]}.

As context/1, memoised per module in the calling process.

Instantiation rebuilds the context each time, and canonicalisation interns every recursive type group. A module is immutable, so the answer cannot change; recomputing it per instance cost a third of instantiation time.

context/1

-spec context(#module{identity :: undefined | {sha256, binary()} | reference(),
                      types ::
                          [#subtype{final :: boolean(),
                                    supers :: [typeidx()],
                                    body ::
                                        #functype{params :: [valtype()], results :: [valtype()]} |
                                        #structtype{fields ::
                                                        [#fieldtype{type :: valtype() | i8 | i16,
                                                                    mut :: mut()}]} |
                                        #arraytype{field ::
                                                       #fieldtype{type :: valtype() | i8 | i16,
                                                                  mut :: mut()}}}],
                      rec_groups :: [{non_neg_integer(), non_neg_integer()}],
                      imports :: [#import{module :: binary(), name :: binary(), desc :: externtype()}],
                      funcs ::
                          [#func{type :: typeidx(),
                                 locals :: [valtype()],
                                 body :: [instr()] | {validated, [annotated()]}}],
                      tables ::
                          [#tabletype{limits ::
                                          #limits{min :: non_neg_integer(),
                                                  max :: undefined | non_neg_integer(),
                                                  shared :: boolean(),
                                                  index_type :: i32 | i64},
                                      elemtype :: reftype(),
                                      init :: undefined | [instr()]}],
                      mems ::
                          [#memtype{limits ::
                                        #limits{min :: non_neg_integer(),
                                                max :: undefined | non_neg_integer(),
                                                shared :: boolean(),
                                                index_type :: i32 | i64}}],
                      tags :: [#tagtype{type :: typeidx()}],
                      globals ::
                          [#global{type :: #globaltype{valtype :: valtype(), mut :: mut()},
                                   init :: [instr()]}],
                      exports ::
                          [#export{name :: binary(),
                                   desc :: {func | table | mem | global | tag, non_neg_integer()}}],
                      start :: undefined | funcidx(),
                      elems ::
                          [#elem{type :: reftype(),
                                 init :: [[instr()]],
                                 mode :: passive | declarative | {active, tableidx(), [instr()]}}],
                      datas ::
                          [#data{init :: binary(), mode :: passive | {active, memidx(), [instr()]}}],
                      data_count :: undefined | non_neg_integer(),
                      customs :: [{binary(), binary()}]}) ->
                 #ctx{types :: tuple(),
                      canon :: tuple(),
                      fields :: tuple(),
                      kinds :: tuple(),
                      supers :: tuple(),
                      funcs :: tuple(),
                      tables :: tuple(),
                      mems :: tuple(),
                      globals :: tuple(),
                      tags :: tuple(),
                      elems :: tuple(),
                      n_datas :: non_neg_integer(),
                      n_imported_globals :: non_neg_integer(),
                      refs :: #{non_neg_integer() => true},
                      shared_globals :: #{non_neg_integer() => true},
                      shared_mems :: #{non_neg_integer() => true},
                      locals :: tuple(),
                      results :: [valtype()]}.

module(M)

-spec module(#module{identity :: undefined | {sha256, binary()} | reference(),
                     types ::
                         [#subtype{final :: boolean(),
                                   supers :: [typeidx()],
                                   body ::
                                       #functype{params :: [valtype()], results :: [valtype()]} |
                                       #structtype{fields ::
                                                       [#fieldtype{type :: valtype() | i8 | i16,
                                                                   mut :: mut()}]} |
                                       #arraytype{field ::
                                                      #fieldtype{type :: valtype() | i8 | i16,
                                                                 mut :: mut()}}}],
                     rec_groups :: [{non_neg_integer(), non_neg_integer()}],
                     imports :: [#import{module :: binary(), name :: binary(), desc :: externtype()}],
                     funcs ::
                         [#func{type :: typeidx(),
                                locals :: [valtype()],
                                body :: [instr()] | {validated, [annotated()]}}],
                     tables ::
                         [#tabletype{limits ::
                                         #limits{min :: non_neg_integer(),
                                                 max :: undefined | non_neg_integer(),
                                                 shared :: boolean(),
                                                 index_type :: i32 | i64},
                                     elemtype :: reftype(),
                                     init :: undefined | [instr()]}],
                     mems ::
                         [#memtype{limits ::
                                       #limits{min :: non_neg_integer(),
                                               max :: undefined | non_neg_integer(),
                                               shared :: boolean(),
                                               index_type :: i32 | i64}}],
                     tags :: [#tagtype{type :: typeidx()}],
                     globals ::
                         [#global{type :: #globaltype{valtype :: valtype(), mut :: mut()},
                                  init :: [instr()]}],
                     exports ::
                         [#export{name :: binary(),
                                  desc :: {func | table | mem | global | tag, non_neg_integer()}}],
                     start :: undefined | funcidx(),
                     elems ::
                         [#elem{type :: reftype(),
                                init :: [[instr()]],
                                mode :: passive | declarative | {active, tableidx(), [instr()]}}],
                     datas :: [#data{init :: binary(), mode :: passive | {active, memidx(), [instr()]}}],
                     data_count :: undefined | non_neg_integer(),
                     customs :: [{binary(), binary()}]}) ->
                {ok,
                 #module{identity :: undefined | {sha256, binary()} | reference(),
                         types ::
                             [#subtype{final :: boolean(),
                                       supers :: [typeidx()],
                                       body ::
                                           #functype{params :: [valtype()], results :: [valtype()]} |
                                           #structtype{fields ::
                                                           [#fieldtype{type :: valtype() | i8 | i16,
                                                                       mut :: mut()}]} |
                                           #arraytype{field ::
                                                          #fieldtype{type :: valtype() | i8 | i16,
                                                                     mut :: mut()}}}],
                         rec_groups :: [{non_neg_integer(), non_neg_integer()}],
                         imports ::
                             [#import{module :: binary(), name :: binary(), desc :: externtype()}],
                         funcs ::
                             [#func{type :: typeidx(),
                                    locals :: [valtype()],
                                    body :: [instr()] | {validated, [annotated()]}}],
                         tables ::
                             [#tabletype{limits ::
                                             #limits{min :: non_neg_integer(),
                                                     max :: undefined | non_neg_integer(),
                                                     shared :: boolean(),
                                                     index_type :: i32 | i64},
                                         elemtype :: reftype(),
                                         init :: undefined | [instr()]}],
                         mems ::
                             [#memtype{limits ::
                                           #limits{min :: non_neg_integer(),
                                                   max :: undefined | non_neg_integer(),
                                                   shared :: boolean(),
                                                   index_type :: i32 | i64}}],
                         tags :: [#tagtype{type :: typeidx()}],
                         globals ::
                             [#global{type :: #globaltype{valtype :: valtype(), mut :: mut()},
                                      init :: [instr()]}],
                         exports ::
                             [#export{name :: binary(),
                                      desc :: {func | table | mem | global | tag, non_neg_integer()}}],
                         start :: undefined | funcidx(),
                         elems ::
                             [#elem{type :: reftype(),
                                    init :: [[instr()]],
                                    mode :: passive | declarative | {active, tableidx(), [instr()]}}],
                         datas ::
                             [#data{init :: binary(), mode :: passive | {active, memidx(), [instr()]}}],
                         data_count :: undefined | non_neg_integer(),
                         customs :: [{binary(), binary()}]}} |
                {error, wasm_error:error()}.

module_unchecked(M)

-spec module_unchecked(#module{identity :: undefined | {sha256, binary()} | reference(),
                               types ::
                                   [#subtype{final :: boolean(),
                                             supers :: [typeidx()],
                                             body ::
                                                 #functype{params :: [valtype()],
                                                           results :: [valtype()]} |
                                                 #structtype{fields ::
                                                                 [#fieldtype{type ::
                                                                                 valtype() | i8 | i16,
                                                                             mut :: mut()}]} |
                                                 #arraytype{field ::
                                                                #fieldtype{type :: valtype() | i8 | i16,
                                                                           mut :: mut()}}}],
                               rec_groups :: [{non_neg_integer(), non_neg_integer()}],
                               imports ::
                                   [#import{module :: binary(), name :: binary(), desc :: externtype()}],
                               funcs ::
                                   [#func{type :: typeidx(),
                                          locals :: [valtype()],
                                          body :: [instr()] | {validated, [annotated()]}}],
                               tables ::
                                   [#tabletype{limits ::
                                                   #limits{min :: non_neg_integer(),
                                                           max :: undefined | non_neg_integer(),
                                                           shared :: boolean(),
                                                           index_type :: i32 | i64},
                                               elemtype :: reftype(),
                                               init :: undefined | [instr()]}],
                               mems ::
                                   [#memtype{limits ::
                                                 #limits{min :: non_neg_integer(),
                                                         max :: undefined | non_neg_integer(),
                                                         shared :: boolean(),
                                                         index_type :: i32 | i64}}],
                               tags :: [#tagtype{type :: typeidx()}],
                               globals ::
                                   [#global{type :: #globaltype{valtype :: valtype(), mut :: mut()},
                                            init :: [instr()]}],
                               exports ::
                                   [#export{name :: binary(),
                                            desc ::
                                                {func | table | mem | global | tag, non_neg_integer()}}],
                               start :: undefined | funcidx(),
                               elems ::
                                   [#elem{type :: reftype(),
                                          init :: [[instr()]],
                                          mode ::
                                              passive | declarative | {active, tableidx(), [instr()]}}],
                               datas ::
                                   [#data{init :: binary(),
                                          mode :: passive | {active, memidx(), [instr()]}}],
                               data_count :: undefined | non_neg_integer(),
                               customs :: [{binary(), binary()}]}) ->
                          #module{identity :: undefined | {sha256, binary()} | reference(),
                                  types ::
                                      [#subtype{final :: boolean(),
                                                supers :: [typeidx()],
                                                body ::
                                                    #functype{params :: [valtype()],
                                                              results :: [valtype()]} |
                                                    #structtype{fields ::
                                                                    [#fieldtype{type ::
                                                                                    valtype() | i8 | i16,
                                                                                mut :: mut()}]} |
                                                    #arraytype{field ::
                                                                   #fieldtype{type ::
                                                                                  valtype() | i8 | i16,
                                                                              mut :: mut()}}}],
                                  rec_groups :: [{non_neg_integer(), non_neg_integer()}],
                                  imports ::
                                      [#import{module :: binary(),
                                               name :: binary(),
                                               desc :: externtype()}],
                                  funcs ::
                                      [#func{type :: typeidx(),
                                             locals :: [valtype()],
                                             body :: [instr()] | {validated, [annotated()]}}],
                                  tables ::
                                      [#tabletype{limits ::
                                                      #limits{min :: non_neg_integer(),
                                                              max :: undefined | non_neg_integer(),
                                                              shared :: boolean(),
                                                              index_type :: i32 | i64},
                                                  elemtype :: reftype(),
                                                  init :: undefined | [instr()]}],
                                  mems ::
                                      [#memtype{limits ::
                                                    #limits{min :: non_neg_integer(),
                                                            max :: undefined | non_neg_integer(),
                                                            shared :: boolean(),
                                                            index_type :: i32 | i64}}],
                                  tags :: [#tagtype{type :: typeidx()}],
                                  globals ::
                                      [#global{type :: #globaltype{valtype :: valtype(), mut :: mut()},
                                               init :: [instr()]}],
                                  exports ::
                                      [#export{name :: binary(),
                                               desc ::
                                                   {func | table | mem | global | tag,
                                                    non_neg_integer()}}],
                                  start :: undefined | funcidx(),
                                  elems ::
                                      [#elem{type :: reftype(),
                                             init :: [[instr()]],
                                             mode ::
                                                 passive | declarative | {active, tableidx(), [instr()]}}],
                                  datas ::
                                      [#data{init :: binary(),
                                             mode :: passive | {active, memidx(), [instr()]}}],
                                  data_count :: undefined | non_neg_integer(),
                                  customs :: [{binary(), binary()}]}.