tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

validation.js (14062B)


      1 // Test wasm type validation for exception handling instructions.
      2 
      3 load(libdir + "wasm-binary.js");
      4 
      5 function wasmValid(mod) {
      6  assertEq(WebAssembly.validate(mod), true);
      7 }
      8 
      9 function wasmInvalid(mod, pattern) {
     10  assertEq(WebAssembly.validate(mod), false);
     11  assertErrorMessage(
     12    () => new WebAssembly.Module(mod),
     13    WebAssembly.CompileError,
     14    pattern
     15  );
     16 }
     17 
     18 const emptyType = { args: [], ret: VoidCode };
     19 const i32Type = { args: [I32Code], ret: VoidCode };
     20 const toi32Type = { args: [], ret: I32Code };
     21 const i32Toi32Type = { args: [I32Code], ret: I32Code };
     22 const i32Toi64Type = { args: [I32Code], ret: I64Code };
     23 const i32i32Toi32Type = { args: [I32Code, I32Code], ret: I32Code };
     24 
     25 function testValidateDecode() {
     26  // Try blocks must have a block type code.
     27  wasmInvalid(
     28    moduleWithSections([
     29      sigSection([emptyType]),
     30      declSection([0]),
     31      tagSection([{ type: 0 }]),
     32      bodySection([
     33        funcBody({
     34          locals: [],
     35          body: [
     36            TryCode,
     37            // Missing type code.
     38            I32ConstCode,
     39            0x01,
     40            CatchCode,
     41            0x00,
     42            EndCode,
     43            DropCode,
     44            ReturnCode,
     45          ],
     46        }),
     47      ]),
     48    ]),
     49    /bad type/
     50  );
     51 
     52  // Catch must have a tag index.
     53  wasmInvalid(
     54    moduleWithSections([
     55      sigSection([emptyType]),
     56      declSection([0]),
     57      tagSection([{ type: 0 }]),
     58      bodySection([
     59        funcBody(
     60          {
     61            locals: [],
     62            body: [
     63              TryCode,
     64              I32Code,
     65              I32ConstCode,
     66              0x01,
     67              CatchCode,
     68              // Index missing.
     69            ],
     70          },
     71          (withEndCode = false)
     72        ),
     73      ]),
     74    ]),
     75    /expected tag index/
     76  );
     77 
     78  // Rethrow must have a depth argument.
     79  wasmInvalid(
     80    moduleWithSections([
     81      sigSection([emptyType]),
     82      declSection([0]),
     83      tagSection([{ type: 0 }]),
     84      bodySection([
     85        funcBody(
     86          {
     87            locals: [],
     88            body: [
     89              RethrowCode,
     90              // Index missing.
     91            ],
     92          },
     93          (withEndCode = false)
     94        ),
     95      ]),
     96    ]),
     97    /unable to read rethrow depth/
     98  );
     99 
    100  // Delegate must have a depth argument.
    101  wasmInvalid(
    102    moduleWithSections([
    103      sigSection([emptyType]),
    104      declSection([0]),
    105      tagSection([{ type: 0 }]),
    106      bodySection([
    107        funcBody(
    108          {
    109            locals: [],
    110            body: [
    111              TryCode,
    112              I32Code,
    113              I32ConstCode,
    114              0x01,
    115              DelegateCode,
    116              // Index missing.
    117            ],
    118          },
    119          (withEndCode = false)
    120        ),
    121      ]),
    122    ]),
    123    /unable to read delegate depth/
    124  );
    125 }
    126 
    127 function testValidateThrow() {
    128  valid = `(module
    129             (type (func (param i32)))
    130             (func $exn-zero
    131               i32.const 0
    132               throw $exn1)
    133             (tag $exn1 (type 0)))`;
    134 
    135  validSimd = `(module
    136                (tag $exn (param v128))
    137                (func (export "f") (param v128) (result v128)
    138                  try (result v128)
    139                    (v128.const f64x2 1 2)
    140                      (throw $exn)
    141                  catch $exn
    142                  end))`;
    143 
    144  invalid0 = `(module
    145                (type (func (param i32)))
    146                (func $exn-zero
    147                  throw $exn1)
    148                (tag $exn1 (type 0)))`;
    149  error0 = /popping value from empty stack/;
    150 
    151  invalid1 = `(module
    152                (type (func (param i32)))
    153                (func $exn-zero
    154                  i64.const 0
    155                  throw $exn1)
    156                (tag $exn1 (type 0)))`;
    157  error1 = /expression has type i64 but expected i32/;
    158 
    159  invalid2 = `(module
    160                (type (func (param i32)))
    161                (func $exn-zero
    162                  i32.const 0
    163                  throw 1)
    164                (tag $exn1 (type 0)))`;
    165  error2 = /tag index out of range/;
    166 
    167  wasmValidateText(valid);
    168  if (wasmSimdEnabled()) {
    169    wasmValidateText(validSimd);
    170  }
    171  wasmFailValidateText(invalid0, error0);
    172  wasmFailValidateText(invalid1, error1);
    173  wasmFailValidateText(invalid2, error2);
    174 }
    175 
    176 function testValidateTryCatch() {
    177  function mod_with(fbody) {
    178    return moduleWithSections([
    179      sigSection([emptyType, i32Type, i32i32Toi32Type]),
    180      declSection([0]),
    181      tagSection([{ type: 0 }, { type: 1 }]),
    182      bodySection([
    183        funcBody({
    184          locals: [],
    185          body: fbody,
    186        }),
    187      ]),
    188    ]);
    189  }
    190 
    191  const body1 = [
    192    // try (result i32)
    193    TryCode,
    194    I32Code,
    195    // (i32.const 1)
    196    I32ConstCode,
    197    varU32(1),
    198    // catch 1
    199    CatchCode,
    200    varU32(1),
    201  ];
    202 
    203  const valid1 = mod_with(body1.concat([EndCode, DropCode, ReturnCode]));
    204  const invalid1 = mod_with(
    205    body1.concat([I32ConstCode, varU32(2), EndCode, DropCode, ReturnCode])
    206  );
    207 
    208  const valid2 = mod_with([
    209    // (i32.const 0) (i32.const 0)
    210    I32ConstCode,
    211    varU32(0),
    212    I32ConstCode,
    213    varU32(0),
    214    // try (param i32 i32) (result i32) drop drop (i32.const 1)
    215    TryCode,
    216    varS32(2),
    217    DropCode,
    218    DropCode,
    219    I32ConstCode,
    220    varU32(1),
    221    // catch 0 (i32.const 2) end drop return
    222    CatchCode,
    223    varU32(0),
    224    I32ConstCode,
    225    varU32(2),
    226    EndCode,
    227    DropCode,
    228    ReturnCode,
    229  ]);
    230 
    231  wasmValid(valid1);
    232  wasmInvalid(invalid1, /unused values not explicitly dropped/);
    233  wasmValid(valid2);
    234 
    235  // Test handler-less try blocks.
    236  wasmValidateText(
    237    `(module (func try end))`
    238  );
    239 
    240  wasmValidateText(
    241    `(module (func (result i32) try (result i32) (i32.const 1) end))`
    242  );
    243 
    244  wasmValidateText(
    245    `(module
    246       (func (result i32)
    247         try (result i32) (i32.const 1) (br 0) end))`
    248  );
    249 
    250  wasmFailValidateText(
    251    `(module
    252       (func try (result i32) end))`,
    253    /popping value from empty stack/
    254  );
    255 }
    256 
    257 function testValidateCatch() {
    258  wasmInvalid(
    259    moduleWithSections([
    260      sigSection([emptyType]),
    261      declSection([0]),
    262      bodySection([
    263        funcBody({
    264          locals: [],
    265          body: [TryCode, VoidCode, CatchCode, varU32(0), EndCode],
    266        }),
    267      ]),
    268    ]),
    269    /tag index out of range/
    270  );
    271 }
    272 
    273 function testValidateCatchAll() {
    274  wasmValidateText(
    275    `(module
    276       (tag $exn)
    277       (func try catch $exn catch_all end))`
    278  );
    279 
    280  wasmValidateText(
    281    `(module
    282       (func (result i32)
    283         try (result i32)
    284           (i32.const 0)
    285         catch_all
    286           (i32.const 1)
    287         end))`
    288  );
    289 
    290  wasmFailValidateText(
    291    `(module
    292       (tag $exn)
    293       (func try catch_all catch 0 end))`,
    294    /catch cannot follow a catch_all/
    295  );
    296 
    297  wasmFailValidateText(
    298    `(module
    299       (tag $exn)
    300       (func try (result i32) (i32.const 1) catch_all end drop))`,
    301    /popping value from empty stack/
    302  );
    303 
    304  wasmFailValidateText(
    305    `(module
    306       (tag $exn (param i32))
    307       (func try catch $exn drop catch_all drop end))`,
    308    /popping value from empty stack/
    309  );
    310 
    311  // We can't distinguish `else` and `catch_all` in error messages since they
    312  // share the binary opcode.
    313  wasmFailValidateText(
    314    `(module
    315       (tag $exn)
    316       (func try catch_all catch_all end))`,
    317    /catch_all can only be used within a try/
    318  );
    319 
    320  wasmFailValidateText(
    321    `(module
    322       (tag $exn)
    323       (func catch_all))`,
    324    /catch_all can only be used within a try/
    325  );
    326 }
    327 
    328 function testValidateExnPayload() {
    329  valid0 = moduleWithSections([
    330    sigSection([i32Type, i32Toi32Type]),
    331    declSection([1]),
    332    // (tag $exn (param i32))
    333    tagSection([{ type: 0 }]),
    334    bodySection([
    335      // (func (param i32) (result i32) ...
    336      funcBody({
    337        locals: [],
    338        body: [
    339          // try (result i32) (local.get 0) (throw $exn) (i32.const 1)
    340          TryCode,
    341          I32Code,
    342          LocalGetCode,
    343          varU32(0),
    344          ThrowCode,
    345          varU32(0),
    346          I32ConstCode,
    347          varU32(1),
    348          // catch $exn (i32.const 1) (i32.add) end
    349          CatchCode,
    350          varU32(0),
    351          I32ConstCode,
    352          varU32(1),
    353          I32AddCode,
    354          EndCode,
    355        ],
    356      }),
    357    ]),
    358  ]);
    359 
    360  // This is to ensure the following sentence from the spec overview holds:
    361  // > "the operand stack is popped back to the size the operand stack had
    362  // > when the try block was entered"
    363  valid1 = moduleWithSections([
    364    sigSection([i32Type, toi32Type]),
    365    declSection([1]),
    366    // (tag $exn (param i32))
    367    tagSection([{ type: 0 }]),
    368    bodySection([
    369      // (func (result i32) ...
    370      funcBody({
    371        locals: [],
    372        body: [
    373          // try (result i32) (i32.const 0) (i32.const 1) (throw $exn) drop
    374          TryCode,
    375          I32Code,
    376          I32ConstCode,
    377          varU32(0),
    378          I32ConstCode,
    379          varU32(1),
    380          ThrowCode,
    381          varU32(0),
    382          DropCode,
    383          // catch $exn drop (i32.const 2) end
    384          CatchCode,
    385          varU32(0),
    386          DropCode,
    387          I32ConstCode,
    388          varU32(2),
    389          EndCode,
    390        ],
    391      }),
    392    ]),
    393  ]);
    394 
    395  invalid0 = moduleWithSections([
    396    sigSection([i32Type, i32Toi64Type]),
    397    declSection([1]),
    398    // (tag $exn (param i32))
    399    tagSection([{ type: 0 }]),
    400    bodySection([
    401      // (func (param i32) (result i64) ...
    402      funcBody({
    403        locals: [],
    404        body: [
    405          // try (result i64) (local.get 0) (throw $exn) (i64.const 0)
    406          TryCode,
    407          I64Code,
    408          LocalGetCode,
    409          varU32(0),
    410          ThrowCode,
    411          varU32(0),
    412          I64ConstCode,
    413          varU32(0),
    414          // catch $exn end
    415          CatchCode,
    416          varU32(0),
    417          EndCode,
    418        ],
    419      }),
    420    ]),
    421  ]);
    422 
    423  invalid1 = moduleWithSections([
    424    // (type (func))
    425    sigSection([emptyType]),
    426    declSection([0]),
    427    // (tag $exn (type 0))
    428    tagSection([{ type: 0 }]),
    429    bodySection([
    430      // (func ...
    431      funcBody({
    432        locals: [],
    433        body: [
    434          // try catch 1 end
    435          TryCode,
    436          VoidCode,
    437          CatchCode,
    438          varU32(1),
    439          EndCode,
    440        ],
    441      }),
    442    ]),
    443  ]);
    444 
    445  wasmValid(valid0);
    446  wasmValid(valid1);
    447  wasmInvalid(invalid0, /has type i32 but expected i64/);
    448  wasmInvalid(invalid1, /tag index out of range/);
    449 }
    450 
    451 function testValidateRethrow() {
    452  wasmValidateText(
    453    `(module
    454       (tag $exn (param))
    455       (func
    456         try
    457           nop
    458         catch $exn
    459           rethrow 0
    460         end))`
    461  );
    462 
    463  wasmValidateText(
    464    `(module
    465       (tag $exn (param))
    466       (func
    467         try
    468           nop
    469         catch_all
    470           rethrow 0
    471         end))`
    472  );
    473 
    474  wasmValidateText(
    475    `(module
    476       (func (result i32)
    477         try (result i32)
    478           (i32.const 1)
    479         catch_all
    480           rethrow 0
    481         end))`
    482  );
    483 
    484  wasmValidateText(
    485    `(module
    486       (tag $exn (param))
    487       (func
    488         try
    489           nop
    490         catch $exn
    491           block
    492             try
    493             catch $exn
    494               rethrow 0
    495             end
    496           end
    497         end))`
    498  );
    499 
    500  wasmValidateText(
    501    `(module
    502       (tag $exn (param))
    503       (func
    504         try
    505           nop
    506         catch $exn
    507           block
    508             try
    509             catch $exn
    510               rethrow 2
    511             end
    512           end
    513         end))`
    514  );
    515 
    516  wasmFailValidateText(
    517    `(module
    518       (tag $exn (param))
    519       (func
    520         try
    521           nop
    522         catch $exn
    523           block
    524             try
    525             catch $exn
    526               rethrow 1
    527             end
    528           end
    529         end))`,
    530    /rethrow target was not a catch block/
    531  );
    532 
    533  wasmFailValidateText(
    534    `(module (func rethrow 0))`,
    535    /rethrow target was not a catch block/
    536  );
    537 
    538  wasmFailValidateText(
    539    `(module (func try rethrow 0 end))`,
    540    /rethrow target was not a catch block/
    541  );
    542 
    543  wasmFailValidateText(
    544    `(module (func try rethrow 0 catch_all end))`,
    545    /rethrow target was not a catch block/
    546  );
    547 
    548  wasmFailValidateText(
    549    `(module
    550       (tag $exn (param))
    551       (func
    552         try
    553           nop
    554         catch $exn
    555           block
    556             try
    557             catch $exn
    558               rethrow 4
    559             end
    560           end
    561         end))`,
    562    /rethrow depth exceeds current nesting level/
    563  );
    564 }
    565 
    566 function testValidateDelegate() {
    567  wasmValidateText(
    568    `(module
    569       (tag $exn (param))
    570       (func
    571         try
    572           try
    573             throw $exn
    574           delegate 0
    575         catch $exn
    576         end))`
    577  );
    578 
    579  wasmValidateText(
    580    `(module
    581       (tag $exn (param))
    582       (func
    583         try
    584           try
    585             throw $exn
    586           delegate 1
    587         catch $exn
    588         end))`
    589  );
    590 
    591  wasmValidateText(
    592    `(module
    593       (tag $exn (param))
    594       (func
    595         block
    596           try
    597             throw $exn
    598           delegate 0
    599         end))`
    600  );
    601 
    602  wasmValidateText(
    603    `(module
    604       (tag $exn (param))
    605       (func
    606         try
    607         catch $exn
    608           try
    609             throw $exn
    610           delegate 0
    611         end))`
    612  );
    613 
    614  wasmFailValidateText(
    615    `(module
    616       (tag $exn (param))
    617       (func (result i32)
    618         try
    619           throw $exn
    620         delegate 0
    621         (i64.const 0)
    622         end))`,
    623    /type mismatch: expression has type i64 but expected i32/
    624  );
    625 
    626  wasmFailValidateText(
    627    `(module
    628       (tag $exn (param))
    629       (func
    630         try (result i32)
    631           (i64.const 0)
    632         delegate 0))`,
    633    /type mismatch: expression has type i64 but expected i32/
    634  );
    635 
    636  wasmFailValidateText(
    637    `(module
    638       (tag $exn (param))
    639       (func
    640         try
    641           try
    642             throw $exn
    643           delegate 2
    644         catch $exn
    645         end))`,
    646    /delegate depth exceeds current nesting level/
    647  );
    648 
    649  wasmFailValidateText(
    650    `(module (func delegate 0))`,
    651    /delegate can only be used within a try/
    652  );
    653 }
    654 
    655 testValidateDecode();
    656 testValidateThrow();
    657 testValidateTryCatch();
    658 testValidateCatch();
    659 testValidateCatchAll();
    660 testValidateExnPayload();
    661 testValidateRethrow();
    662 testValidateDelegate();