tor-browser

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

passive-segs-boundary.js (17176B)


      1 // Perform a test which,
      2 //
      3 // * if errKind is defined, is expected to fail with an exception
      4 //   characterised by errKind and errText.
      5 //
      6 // * if errKind is undefined, is expected to succeed, in which case errKind
      7 //   and errText are ignored.
      8 //
      9 // The function body will be [insn1, insn2] and is constructed according to
     10 // four booleans:
     11 //
     12 // * isMem controls whether the module is constructed with memory or
     13 //   table initializers.
     14 //
     15 // * haveStorage determines whether there is actually a memory or table to
     16 //   work with.
     17 //
     18 // * haveInitA controls whether active initializers are added.
     19 //
     20 // * haveInitP controls whether passive initializers are added.
     21 
     22 function do_test(insn1, insn2, errKind, errText,
     23                 isMem, haveStorage, haveInitA, haveInitP)
     24 {
     25    let preamble;
     26    if (isMem) {
     27        let mem_def  = haveStorage ? "(memory 1 1)" : "";
     28        let mem_ia1  = `(data (i32.const 2) "\\03\\01\\04\\01")`;
     29        let mem_ia2  = `(data (i32.const 12) "\\07\\05\\02\\03\\06")`;
     30        let mem_ip1  = `(data "\\02\\07\\01\\08")`;
     31        let mem_ip2  = `(data "\\05\\09\\02\\07\\06")`;
     32        let mem_init = ``;
     33        if (haveInitA && haveInitP)
     34            mem_init = `${mem_ia1} ${mem_ip1} ${mem_ia2} ${mem_ip2}`;
     35        else if (haveInitA && !haveInitP) mem_init = `${mem_ia1} ${mem_ia2}`;
     36        else if (!haveInitA && haveInitP) mem_init = `${mem_ip1} ${mem_ip2}`;
     37        preamble
     38            = `;; -------- Memories --------
     39               ${mem_def}
     40               ;; -------- Memory initialisers --------
     41               ${mem_init}
     42              `;
     43    } else {
     44        let tab_def  = haveStorage ? "(table 30 30 funcref)" : "";
     45        let tab_ia1  = `(elem (i32.const 2) 3 1 4 1)`;
     46        let tab_ia2  = `(elem (i32.const 12) 7 5 2 3 6)`;
     47        let tab_ip1  = `(elem func 2 7 1 8)`;
     48        let tab_ip2  = `(elem func 5 9 2 7 6)`;
     49        let tab_init = ``;
     50        if (haveInitA && haveInitP)
     51            tab_init = `${tab_ia1} ${tab_ip1} ${tab_ia2} ${tab_ip2}`;
     52        else if (haveInitA && !haveInitP) tab_init = `${tab_ia1} ${tab_ia2}`;
     53        else if (!haveInitA && haveInitP) tab_init = `${tab_ip1} ${tab_ip2}`;
     54        preamble
     55            = `;; -------- Tables --------
     56               ${tab_def}
     57               ;; -------- Table initialisers --------
     58               ${tab_init}
     59               ;; ------ Functions (0..9) referred by the table/esegs ------
     60               (func (result i32) (i32.const 0))
     61               (func (result i32) (i32.const 1))
     62               (func (result i32) (i32.const 2))
     63               (func (result i32) (i32.const 3))
     64               (func (result i32) (i32.const 4))
     65               (func (result i32) (i32.const 5))
     66               (func (result i32) (i32.const 6))
     67               (func (result i32) (i32.const 7))
     68               (func (result i32) (i32.const 8))
     69               (func (result i32) (i32.const 9))
     70              `;
     71    }
     72 
     73    let txt = "(module\n" + preamble +
     74              `;; -------- testfn --------
     75               (func (export "testfn")
     76                 ${insn1}
     77                 ${insn2}
     78               )
     79               )`;
     80 
     81    if (!!errKind) {
     82        assertErrorMessage(
     83            () => {
     84                let inst = wasmEvalText(txt);
     85                inst.exports.testfn();
     86            },
     87            errKind,
     88            errText
     89        );
     90    } else {
     91        let inst = wasmEvalText(txt);
     92        assertEq(undefined, inst.exports.testfn());
     93    }
     94 }
     95 
     96 
     97 // Some handy specialisations of do_test().
     98 
     99 function mem_test(insn1, insn2, errKind, errText,
    100                  haveStorage=true, haveInitA=true, haveInitP=true) {
    101    do_test(insn1, insn2, errKind, errText,
    102            /*isMem=*/true, haveStorage, haveInitA, haveInitP);
    103 }
    104 
    105 function mem_test_nofail(insn1, insn2,
    106                         haveStorage=true, haveInitA=true, haveInitP=true) {
    107    do_test(insn1, insn2, undefined, undefined,
    108            /*isMem=*/true, haveStorage, haveInitA, haveInitP);
    109 }
    110 
    111 function tab_test(insn1, insn2, errKind, errText,
    112                  haveStorage=true, haveInitA=true, haveInitP=true) {
    113    do_test(insn1, insn2, errKind, errText,
    114            /*isMem=*/false, haveStorage, haveInitA, haveInitP);
    115 }
    116 
    117 function tab_test_nofail(insn1, insn2,
    118                         haveStorage=true, haveInitA=true, haveInitP=true) {
    119    do_test(insn1, insn2, undefined, undefined,
    120            /*isMem=*/false, haveStorage, haveInitA, haveInitP);
    121 }
    122 
    123 
    124 //---- memory.{drop,init,copy} -------------------------------------------------
    125 
    126 // The tested semantics for memory.drop, in the case where there's no memory,
    127 // are as follows.  table.drop is analogous.
    128 //
    129 // no memory, no data segments:
    130 //    drop with any args -> fail OOB
    131 //                          [because there's nothing to drop]
    132 //
    133 // no memory, data segments, at least one of which is active:
    134 //    -> always fails, regardless of insns
    135 //       [because active segments implicitly reference memory 0]
    136 //
    137 // no memory, data segments, all of which are passive:
    138 //    drop, segment index is OOB -> fail OOB
    139 //                                  [because it refers to non existent segment]
    140 //
    141 //    drop, segment index is IB -> OK
    142 
    143 // drop with no memory and no data segments
    144 mem_test("data.drop 0", "",
    145         WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 0)/,
    146         /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
    147 
    148 // drop with no memory but with both passive and active segments, ix in range
    149 // and refers to a passive segment
    150 mem_test("data.drop 3", "",
    151         WebAssembly.CompileError,
    152         /active data segment requires a memory section/,
    153         /*haveStorage=*/false, /*haveInitA=*/true, /*haveInitP=*/true);
    154 
    155 // drop with no memory but with passive segments only, ix out of range
    156 mem_test("data.drop 2", "",
    157         WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 2)/,
    158         /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
    159 
    160 // drop with no memory but with passive segments only, ix in range
    161 mem_test_nofail("data.drop 1", "",
    162                /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
    163 
    164 
    165 // init with no memory and no data segs
    166 mem_test("(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
    167         WebAssembly.CompileError, /memory index/,
    168         /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
    169 
    170 // drop with data seg ix out of range
    171 mem_test("data.drop 4", "",
    172         WebAssembly.CompileError, /(data.drop segment index out of range)|(unknown data segment 4)/);
    173 
    174 // init with data seg ix out of range
    175 mem_test("(memory.init 4 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
    176         WebAssembly.CompileError, /(memory.init segment index out of range)|(unknown data segment 4)/);
    177 
    178 // drop with data seg ix indicating an active segment
    179 mem_test("data.drop 2", "");
    180 
    181 // init with data seg ix indicating an active segment
    182 mem_test("(memory.init 2 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
    183         WebAssembly.RuntimeError, /index out of bounds/);
    184 
    185 // init, using a data seg ix more than once is OK
    186 mem_test_nofail(
    187    "(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))",
    188    "(memory.init 1 (i32.const 4321) (i32.const 1) (i32.const 1))");
    189 
    190 // drop, then drop
    191 mem_test("data.drop 1",
    192         "data.drop 1");
    193 
    194 // drop, then init
    195 mem_test("data.drop 1",
    196         "(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))",
    197         WebAssembly.RuntimeError, /index out of bounds/);
    198 
    199 // init: seg ix is valid passive, but length to copy > len of seg
    200 mem_test("",
    201         "(memory.init 1 (i32.const 1234) (i32.const 0) (i32.const 5))",
    202         WebAssembly.RuntimeError, /index out of bounds/);
    203 
    204 // init: seg ix is valid passive, but implies copying beyond end of seg
    205 mem_test("",
    206         "(memory.init 1 (i32.const 1234) (i32.const 2) (i32.const 3))",
    207         WebAssembly.RuntimeError, /index out of bounds/);
    208 
    209 // init: seg ix is valid passive, but implies copying beyond end of dst
    210 mem_test("",
    211         "(memory.init 1 (i32.const 0xFFFE) (i32.const 1) (i32.const 3))",
    212         WebAssembly.RuntimeError, /index out of bounds/);
    213 
    214 // init: seg ix is valid passive, zero len, but src offset out of bounds at the
    215 // edge
    216 mem_test("",
    217         "(memory.init 1 (i32.const 1234) (i32.const 4) (i32.const 0))");
    218 
    219 // init: seg ix is valid passive, zero len, but src offset out of bounds one
    220 // past the edge
    221 mem_test("",
    222         "(memory.init 1 (i32.const 1234) (i32.const 5) (i32.const 0))",
    223         WebAssembly.RuntimeError, /index out of bounds/);
    224 
    225 // init: seg ix is valid passive, zero len, but dst offset out of bounds at the
    226 // edge
    227 mem_test("",
    228         "(memory.init 1 (i32.const 0x10000) (i32.const 2) (i32.const 0))");
    229 
    230 // init: seg ix is valid passive, zero len, but dst offset out of bounds one
    231 // past the edge
    232 mem_test("",
    233         "(memory.init 1 (i32.const 0x10001) (i32.const 2) (i32.const 0))",
    234         WebAssembly.RuntimeError, /index out of bounds/);
    235 
    236 // drop: too many args
    237 mem_test("data.drop 1 (i32.const 42)", "",
    238         WebAssembly.CompileError,
    239         /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
    240 
    241 // init: too many args
    242 mem_test("(memory.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
    243         "",
    244         WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/);
    245 
    246 // init: too few args
    247 mem_test("(memory.init 1 (i32.const 1) (i32.const 1))", "",
    248         WebAssembly.CompileError,
    249         /(popping value from empty stack)|(expected i32 but nothing on stack)/);
    250 
    251 // invalid argument types
    252 {
    253    const tys  = ['i32', 'f32', 'i64', 'f64'];
    254 
    255    for (let ty1 of tys) {
    256    for (let ty2 of tys) {
    257    for (let ty3 of tys) {
    258        if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
    259            continue;  // this is the only valid case
    260        let i1 = `(memory.init 1 (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`;
    261        mem_test(i1, "", WebAssembly.CompileError, /type mismatch/);
    262    }}}
    263 }
    264 
    265 //
    266 //---- table.{drop,init} --------------------------------------------------
    267 
    268 // drop with no tables and no elem segments
    269 tab_test("elem.drop 0", "",
    270         WebAssembly.CompileError,
    271         /(element segment index out of range for elem.drop)|(segment index out of bounds)/,
    272         /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
    273 
    274 // drop with no tables but with both passive and active segments, ix in range
    275 // and refers to a passive segment
    276 tab_test("elem.drop 3", "",
    277         WebAssembly.CompileError,
    278         /active elem segment requires a table/,
    279         /*haveStorage=*/false, /*haveInitA=*/true, /*haveInitP=*/true);
    280 
    281 // drop with no tables but with passive segments only, ix out of range
    282 tab_test("elem.drop 2", "",
    283         WebAssembly.CompileError,
    284         /(element segment index out of range for elem.drop)|(segment index out of bounds)/,
    285         /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
    286 
    287 // drop with no tables but with passive segments only, ix in range
    288 tab_test_nofail("elem.drop 1", "",
    289                /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/true);
    290 
    291 
    292 // init with no table
    293 tab_test("(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", "",
    294         WebAssembly.CompileError, /(table index out of range)|(table index out of bounds)/,
    295         /*haveStorage=*/false, /*haveInitA=*/false, /*haveInitP=*/false);
    296 
    297 // drop with elem seg ix out of range
    298 tab_test("elem.drop 4", "",
    299         WebAssembly.CompileError, /(element segment index out of range for elem.drop)|(segment index out of bounds)/);
    300 
    301 // init with elem seg ix out of range
    302 tab_test("(table.init 4 (i32.const 12) (i32.const 1) (i32.const 1))", "",
    303         WebAssembly.CompileError, /(table.init segment index out of range)|(segment index out of bounds)/);
    304 
    305 // drop with elem seg ix indicating an active segment
    306 tab_test("elem.drop 2", "");
    307 
    308 // init with elem seg ix indicating an active segment
    309 tab_test("(table.init 2 (i32.const 12) (i32.const 1) (i32.const 1))", "",
    310         WebAssembly.RuntimeError, /index out of bounds/);
    311 
    312 // init, using an elem seg ix more than once is OK
    313 tab_test_nofail(
    314    "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))",
    315    "(table.init 1 (i32.const 21) (i32.const 1) (i32.const 1))");
    316 
    317 // drop, then drop
    318 tab_test("elem.drop 1",
    319         "elem.drop 1");
    320 
    321 // drop, then init
    322 tab_test("elem.drop 1",
    323         "(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))",
    324         WebAssembly.RuntimeError, /index out of bounds/);
    325 
    326 // init: seg ix is valid passive, but length to copy > len of seg
    327 tab_test("",
    328         "(table.init 1 (i32.const 12) (i32.const 0) (i32.const 5))",
    329         WebAssembly.RuntimeError, /index out of bounds/);
    330 
    331 // init: seg ix is valid passive, but implies copying beyond end of seg
    332 tab_test("",
    333         "(table.init 1 (i32.const 12) (i32.const 2) (i32.const 3))",
    334         WebAssembly.RuntimeError, /index out of bounds/);
    335 
    336 // init: seg ix is valid passive, but implies copying beyond end of dst
    337 tab_test("",
    338         "(table.init 1 (i32.const 28) (i32.const 1) (i32.const 3))",
    339         WebAssembly.RuntimeError, /index out of bounds/);
    340 
    341 // init: seg ix is valid passive, zero len, but src offset out of bounds at the
    342 // edge
    343 tab_test("",
    344         "(table.init 1 (i32.const 12) (i32.const 4) (i32.const 0))");
    345 
    346 // init: seg ix is valid passive, zero len, but src offset out of bounds one
    347 // past the edge
    348 tab_test("",
    349         "(table.init 1 (i32.const 12) (i32.const 5) (i32.const 0))",
    350         WebAssembly.RuntimeError, /index out of bounds/);
    351 
    352 // init: seg ix is valid passive, zero len, but dst offset out of bounds
    353 tab_test("",
    354         "(table.init 1 (i32.const 30) (i32.const 2) (i32.const 0))");
    355 
    356 // init: seg ix is valid passive, zero len, but dst offset out of bounds one
    357 // past the edge
    358 tab_test("",
    359         "(table.init 1 (i32.const 31) (i32.const 2) (i32.const 0))",
    360         WebAssembly.RuntimeError, /index out of bounds/);
    361 
    362 // drop: too many args
    363 tab_test("elem.drop 1 (i32.const 42)", "",
    364         WebAssembly.CompileError,
    365         /(unused values not explicitly dropped by end of block)|(values remaining on stack at end of block)/);
    366 
    367 // init: too many args
    368 tab_test("(table.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
    369         "",
    370         WebAssembly.CompileError, /(unused values)|(values remaining on stack at end of block)/);
    371 
    372 // init: too few args
    373 tab_test("(table.init 1 (i32.const 1) (i32.const 1))", "",
    374         WebAssembly.CompileError,
    375         /(popping value from empty stack)|(expected i32 but nothing on stack)/);
    376 
    377 // invalid argument types
    378 {
    379    const tys  = ['i32', 'f32', 'i64', 'f64'];
    380 
    381    const ops = ['table.init 1', 'table.copy'];
    382    for (let ty1 of tys) {
    383    for (let ty2 of tys) {
    384    for (let ty3 of tys) {
    385    for (let op of ops) {
    386        if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
    387            continue;  // this is the only valid case
    388        let i1 = `(${op} (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`;
    389        tab_test(i1, "", WebAssembly.CompileError, /type mismatch/);
    390    }}}}
    391 }
    392 
    393 
    394 //---- table.copy ---------------------------------------------------------
    395 
    396 // There are no immediates here, only 3 dynamic args.  So we're limited to
    397 // runtime boundary checks.
    398 
    399 // passive-segs-smoketest.js tests the normal, non-exception cases of
    400 // table.copy.  Here we just test the boundary-failure cases.  The
    401 // table's valid indices are 0 .. 29 inclusive.
    402 
    403 // copy: dst range invalid
    404 tab_test("(table.copy (i32.const 28) (i32.const 1) (i32.const 3))",
    405         "",
    406         WebAssembly.RuntimeError, /index out of bounds/);
    407 
    408 // copy: dst wraparound end of 32 bit offset space
    409 tab_test("(table.copy (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2))",
    410         "",
    411         WebAssembly.RuntimeError, /index out of bounds/);
    412 
    413 // copy: src range invalid
    414 tab_test("(table.copy (i32.const 15) (i32.const 25) (i32.const 6))",
    415         "",
    416         WebAssembly.RuntimeError, /index out of bounds/);
    417 
    418 // copy: src wraparound end of 32 bit offset space
    419 tab_test("(table.copy (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2))",
    420         "",
    421         WebAssembly.RuntimeError, /index out of bounds/);
    422 
    423 // copy: zero length with both offsets in-bounds is OK
    424 tab_test_nofail(
    425    "(table.copy (i32.const 15) (i32.const 25) (i32.const 0))",
    426    "");
    427 
    428 // copy: zero length with dst offset out of bounds at the edge
    429 tab_test("(table.copy (i32.const 30) (i32.const 15) (i32.const 0))",
    430         "");
    431 
    432 // copy: zero length with dst offset out of bounds one past the edge
    433 tab_test("(table.copy (i32.const 31) (i32.const 15) (i32.const 0))",
    434         "", WebAssembly.RuntimeError, /index out of bounds/);
    435 
    436 // copy: zero length with src offset out of bounds at the edge
    437 tab_test("(table.copy (i32.const 15) (i32.const 30) (i32.const 0))",
    438         "");
    439 
    440 // copy: zero length with src offset out of bounds one past the edge
    441 tab_test("(table.copy (i32.const 15) (i32.const 31) (i32.const 0))",
    442         "", WebAssembly.RuntimeError, /index out of bounds/);