tor-browser

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

tables.js (18624B)


      1 const Module = WebAssembly.Module;
      2 const Instance = WebAssembly.Instance;
      3 const Table = WebAssembly.Table;
      4 const Memory = WebAssembly.Memory;
      5 const LinkError = WebAssembly.LinkError;
      6 const RuntimeError = WebAssembly.RuntimeError;
      7 
      8 const badFuncRefError = /can only pass WebAssembly exported functions to funcref/;
      9 
     10 function assertSegmentFitError(f) {
     11    assertErrorMessage(f, RuntimeError, /out of bounds/);
     12 }
     13 
     14 var callee = i => `(func $f${i} (result i32) (i32.const ${i}))`;
     15 
     16 wasmFailValidateText(`(module (elem (i32.const 0) $f0) ${callee(0)})`, /elem segment requires a table/);
     17 wasmFailValidateText(`(module (table 10 funcref) (elem (i32.const 0) 0))`, /element index out of range/);
     18 wasmFailValidateText(`(module (table 10 funcref) (func) (elem (i32.const 0) 0 1))`, /element index out of range/);
     19 wasmFailValidateText(`(module (table 10 funcref) (func) (elem (f32.const 0) 0) ${callee(0)})`, /type mismatch/);
     20 
     21 assertSegmentFitError(() => wasmEvalText(`(module (table 10 funcref) (elem (i32.const 10) $f0) ${callee(0)})`));
     22 assertSegmentFitError(() => wasmEvalText(`(module (table 10 funcref) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`));
     23 assertSegmentFitError(() => wasmEvalText(`(module (table 0 funcref) (func) (elem (i32.const 0x10001)))`));
     24 
     25 assertSegmentFitError(() => wasmEvalText(`(module (import "globals" "a" (global i32)) (table 10 funcref) (elem (global.get 0) $f0) ${callee(0)})`, {globals:{a:10}}));
     26 assertSegmentFitError(() => wasmEvalText(`(module (import "globals" "a" (global i32)) (table 10 funcref) (elem (global.get 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}));
     27 
     28 assertEq(new Module(wasmTextToBinary(`(module (table 10 funcref) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)) instanceof Module, true);
     29 assertEq(new Module(wasmTextToBinary(`(module (table 10 funcref) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)) instanceof Module, true);
     30 wasmEvalText(`(module (import "globals" "a" (global i32)) (table 10 funcref) (elem (i32.const 1) $f0 $f0) (elem (global.get 0) $f0) ${callee(0)})`, {globals:{a:0}});
     31 wasmEvalText(`(module (import "globals" "a" (global i32)) (table 10 funcref) (elem (global.get 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}});
     32 
     33 // Explicit table index in a couple of ways, note this requires us to talk about the table type also.
     34 assertEq(new Module(wasmTextToBinary(`(module
     35                                        (table 10 funcref)
     36                                        (table 10 funcref)
     37                                        (elem 1 (i32.const 1) func $f0 $f0)
     38                                        ${callee(0)})`)) instanceof Module, true);
     39 assertEq(new Module(wasmTextToBinary(`(module
     40                                        (table 10 funcref)
     41                                        (table 10 funcref)
     42                                        (elem (table 1) (i32.const 1) func $f0 $f0)
     43                                        ${callee(0)})`)) instanceof Module, true);
     44 
     45 // With "funcref" rather than "func".
     46 assertEq(new Module(wasmTextToBinary(`(module
     47                                        (table 10 funcref)
     48                                        (table 10 funcref)
     49                                        (elem (table 1) (i32.const 1) funcref (ref.func $f0) (ref.func $f0))
     50                                        ${callee(0)})`)) instanceof Module, true);
     51 
     52 // Syntax for the offset, ditto.
     53 assertEq(new Module(wasmTextToBinary(`(module
     54                                        (table 10 funcref)
     55                                        (table 10 funcref)
     56                                        (elem 1 (offset (i32.const 1)) func $f0 $f0)
     57                                        ${callee(0)})`)) instanceof Module, true);
     58 
     59 var m = new Module(wasmTextToBinary(`
     60    (module
     61        (import "globals" "table" (table 10 funcref))
     62        (import "globals" "a" (global i32))
     63        (elem (global.get 0) $f0 $f0)
     64        ${callee(0)})
     65 `));
     66 var tbl = new Table({initial:50, element:"anyfunc"});
     67 assertEq(new Instance(m, {globals:{a:20, table:tbl}}) instanceof Instance, true);
     68 assertSegmentFitError(() => new Instance(m, {globals:{a:50, table:tbl}}));
     69 
     70 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect (type $v2i) (local.get $i))) (export "call" (func $call))`
     71 var callee = i => `(func $f${i} (type $v2i) (i32.const ${i}))`;
     72 
     73 var call = wasmEvalText(`(module (table 10 funcref) ${callee(0)} ${caller})`).exports.call;
     74 assertErrorMessage(() => call(0), RuntimeError, /indirect call to null/);
     75 assertErrorMessage(() => call(10), RuntimeError, /index out of bounds/);
     76 
     77 var call = wasmEvalText(`(module (table 10 funcref) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
     78 assertErrorMessage(() => call(0), RuntimeError, /indirect call to null/);
     79 assertErrorMessage(() => call(10), RuntimeError, /index out of bounds/);
     80 
     81 var call = wasmEvalText(`(module (table 10 funcref) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`).exports.call;
     82 assertEq(call(0), 0);
     83 assertErrorMessage(() => call(1), RuntimeError, /indirect call to null/);
     84 assertErrorMessage(() => call(2), RuntimeError, /indirect call to null/);
     85 assertErrorMessage(() => call(10), RuntimeError, /index out of bounds/);
     86 
     87 var call = wasmEvalText(`(module (table 10 funcref) (elem (i32.const 1) $f0 $f1) (elem (i32.const 4) $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
     88 assertErrorMessage(() => call(0), RuntimeError, /indirect call to null/);
     89 assertEq(call(1), 0);
     90 assertEq(call(2), 1);
     91 assertErrorMessage(() => call(3), RuntimeError, /indirect call to null/);
     92 assertEq(call(4), 0);
     93 assertEq(call(5), 2);
     94 assertErrorMessage(() => call(6), RuntimeError, /indirect call to null/);
     95 assertErrorMessage(() => call(10), RuntimeError, /index out of bounds/);
     96 
     97 var imports = {a:{b:()=>42}};
     98 var call = wasmEvalText(`(module (import "a" "b" (func $f1)) (import "a" "b" (func $f2 (result i32))) (table 10 funcref) (elem (i32.const 0) $f0 $f1 $f2) ${callee(0)} ${caller})`, imports).exports.call;
     99 assertEq(call(0), 0);
    100 assertErrorMessage(() => call(1), RuntimeError, /indirect call signature mismatch/);
    101 assertEq(call(2), 42);
    102 
    103 var tbl = new Table({initial:3, element:"anyfunc"});
    104 var call = wasmEvalText(`(module (import "a" "b" (table 3 funcref)) (export "tbl" (table 0)) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
    105 assertEq(call(0), 0);
    106 assertEq(call(1), 1);
    107 assertEq(tbl.get(0)(), 0);
    108 assertEq(tbl.get(1)(), 1);
    109 assertErrorMessage(() => call(2), RuntimeError, /indirect call to null/);
    110 assertEq(tbl.get(2), null);
    111 
    112 var exp = wasmEvalText(`(module (import "a" "b" (table 3 funcref)) (export "tbl" (table 0)) (elem (i32.const 2) $f2) ${callee(2)} ${caller})`, {a:{b:tbl}}).exports;
    113 assertEq(exp.tbl, tbl);
    114 assertEq(exp.call(0), 0);
    115 assertEq(exp.call(1), 1);
    116 assertEq(exp.call(2), 2);
    117 assertEq(call(0), 0);
    118 assertEq(call(1), 1);
    119 assertEq(call(2), 2);
    120 assertEq(tbl.get(0)(), 0);
    121 assertEq(tbl.get(1)(), 1);
    122 assertEq(tbl.get(2)(), 2);
    123 
    124 var exp1 = wasmEvalText(`(module (table 10 funcref) (export "tbl" (table 0)) (elem (i32.const 0) $f0 $f0) ${callee(0)} (export "f0" (func $f0)) ${caller})`).exports
    125 assertEq(exp1.tbl.get(0), exp1.f0);
    126 assertEq(exp1.tbl.get(1), exp1.f0);
    127 assertEq(exp1.tbl.get(2), null);
    128 assertEq(exp1.call(0), 0);
    129 assertEq(exp1.call(1), 0);
    130 assertErrorMessage(() => exp1.call(2), RuntimeError, /indirect call to null/);
    131 var exp2 = wasmEvalText(`(module (import "a" "b" (table 10 funcref)) (export "tbl" (table 0)) (elem (i32.const 1) $f1 $f1) ${callee(1)} (export "f1" (func $f1)) ${caller})`, {a:{b:exp1.tbl}}).exports
    132 assertEq(exp1.tbl, exp2.tbl);
    133 assertEq(exp2.tbl.get(0), exp1.f0);
    134 assertEq(exp2.tbl.get(1), exp2.f1);
    135 assertEq(exp2.tbl.get(2), exp2.f1);
    136 assertEq(exp1.call(0), 0);
    137 assertEq(exp1.call(1), 1);
    138 assertEq(exp1.call(2), 1);
    139 assertEq(exp2.call(0), 0);
    140 assertEq(exp2.call(1), 1);
    141 assertEq(exp2.call(2), 1);
    142 
    143 var tbl = new Table({initial:3, element:"anyfunc"});
    144 var e1 = wasmEvalText(`(module (func $f (result i32) (i32.const 42)) (export "f" (func $f)))`).exports;
    145 var e2 = wasmEvalText(`(module (func $g (result f32) (f32.const 10)) (export "g" (func $g)))`).exports;
    146 var e3 = wasmEvalText(`(module (func $h (result i32) (i32.const 13)) (export "h" (func $h)))`).exports;
    147 tbl.set(0, e1.f);
    148 tbl.set(1, e2.g);
    149 tbl.set(2, e3.h);
    150 var e4 = wasmEvalText(`(module (import "a" "b" (table 3 funcref)) ${caller})`, {a:{b:tbl}}).exports;
    151 assertEq(e4.call(0), 42);
    152 assertErrorMessage(() => e4.call(1), RuntimeError, /indirect call signature mismatch/);
    153 assertEq(e4.call(2), 13);
    154 
    155 var asmjsFun = (function() { "use asm"; function f() {} return f })();
    156 assertEq(isAsmJSFunction(asmjsFun), isAsmJSCompilationAvailable());
    157 assertErrorMessage(() => tbl.set(0, asmjsFun), TypeError, badFuncRefError);
    158 assertErrorMessage(() => tbl.grow(1, asmjsFun), TypeError, badFuncRefError);
    159 
    160 var m = new Module(wasmTextToBinary(`(module
    161    (type $i2i (func (param i32) (result i32)))
    162    (import "a" "mem" (memory 1))
    163    (import "a" "tbl" (table 10 funcref))
    164    (import "a" "imp" (func $imp (result i32)))
    165    (func $call (param $i i32) (result i32)
    166        (i32.add
    167            (call $imp)
    168            (i32.add
    169                (i32.load (i32.const 0))
    170                (if (result i32) (i32.eqz (local.get $i))
    171                    (then (i32.const 0))
    172                    (else
    173                        (local.set $i (i32.sub (local.get $i) (i32.const 1)))
    174                        (call_indirect (type $i2i) (local.get $i) (local.get $i)))))))
    175    (export "call" (func $call))
    176 )`));
    177 var failTime = false;
    178 var tbl = new Table({initial:10, element:"anyfunc"});
    179 var mem1 = new Memory({initial:1});
    180 var e1 = new Instance(m, {a:{mem:mem1, tbl, imp() {if (failTime) throw new Error("ohai"); return 1}}}).exports;
    181 tbl.set(0, e1.call);
    182 var mem2 = new Memory({initial:1});
    183 var e2 = new Instance(m, {a:{mem:mem2, tbl, imp() {return 10} }}).exports;
    184 tbl.set(1, e2.call);
    185 var mem3 = new Memory({initial:1});
    186 var e3 = new Instance(m, {a:{mem:mem3, tbl, imp() {return 100} }}).exports;
    187 new Int32Array(mem1.buffer)[0] = 1000;
    188 new Int32Array(mem2.buffer)[0] = 10000;
    189 new Int32Array(mem3.buffer)[0] = 100000;
    190 assertEq(e3.call(2), 111111);
    191 failTime = true;
    192 assertErrorMessage(() => e3.call(2), Error, "ohai");
    193 
    194 // Call signatures are matched structurally:
    195 
    196 var call = wasmEvalText(`(module
    197    (type $v2i1 (func (result i32)))
    198    (type $v2i2 (func (result i32)))
    199    (type $i2v (func (param i32)))
    200    (table funcref (elem $a $b $c))
    201    (func $a (type $v2i1) (i32.const 0))
    202    (func $b (type $v2i2) (i32.const 1))
    203    (func $c (type $i2v))
    204    (func $call (param i32) (result i32) (call_indirect (type $v2i1) (local.get 0)))
    205    (export "call" (func $call))
    206 )`).exports.call;
    207 assertEq(call(0), 0);
    208 assertEq(call(1), 1);
    209 assertErrorMessage(() => call(2), RuntimeError, /indirect call signature mismatch/);
    210 
    211 var call = wasmEvalText(`(module
    212    (type $A (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    213    (type $B (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    214    (type $C (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    215    (type $D (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    216    (type $E (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    217    (type $F (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    218    (type $G (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
    219    (table funcref (elem $a $b $c $d $e $f $g))
    220    (func $a (type $A) (local.get 7))
    221    (func $b (type $B) (local.get 8))
    222    (func $c (type $C) (local.get 9))
    223    (func $d (type $D) (local.get 10))
    224    (func $e (type $E) (local.get 11))
    225    (func $f (type $F) (local.get 12))
    226    (func $g (type $G) (local.get 13))
    227    (func $call (param i32) (result i32)
    228        (call_indirect (type $A) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 42) (local.get 0)))
    229    (export "call" (func $call))
    230 )`).exports.call;
    231 assertEq(call(0), 42);
    232 for (var i = 1; i < 7; i++)
    233    assertErrorMessage(() => call(i), RuntimeError, /indirect call signature mismatch/);
    234 assertErrorMessage(() => call(7), RuntimeError, /index out of bounds/);
    235 
    236 // Function identity isn't lost:
    237 var tbl = wasmEvalText(`(module (table (export "tbl") funcref (elem $f)) (func $f))`).exports.tbl;
    238 tbl.get(0).foo = 42;
    239 gc();
    240 assertEq(tbl.get(0).foo, 42);
    241 
    242 (function testCrossRealmCall() {
    243    var g = newGlobal({sameCompartmentAs: this});
    244 
    245    // The memory.size builtin asserts cx->realm matches instance->realm so
    246    // we call it here.
    247    var src = `
    248        (module
    249            (import "a" "t" (table 3 funcref))
    250            (import "a" "m" (memory 1))
    251            (func $f (result i32) (i32.add (i32.const 3) (memory.size)))
    252            (elem (i32.const 0) $f))
    253    `;
    254    g.mem = new Memory({initial:4});
    255    g.tbl = new Table({initial:3, element:"anyfunc"});
    256    var i1 = g.evaluate("new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`" + src + "`)), {a:{t:tbl,m:mem}})");
    257 
    258    var call = new Instance(new Module(wasmTextToBinary(`
    259        (module
    260            (import "a" "t" (table 3 funcref))
    261            (import "a" "m" (memory 1))
    262            (type $v2i (func (result i32)))
    263            (func $call (param $i i32) (result i32) (i32.add (call_indirect (type $v2i) (local.get $i)) (memory.size)))
    264            (export "call" (func $call)))
    265    `)), {a:{t:g.tbl,m:g.mem}}).exports.call;
    266 
    267    for (var i = 0; i < 10; i++)
    268        assertEq(call(0), 11);
    269 })();
    270 
    271 
    272 // Test active segments with a table index.
    273 
    274 {
    275    function makeIt(flag, tblindex) {
    276        return new Uint8Array([0x00, 0x61, 0x73, 0x6d,
    277                               0x01, 0x00, 0x00, 0x00,
    278                               0x04,                   // Table section
    279                               0x04,                   // Section size
    280                               0x01,                   // One table
    281                               0x70,                   // Type: FuncRef
    282                               0x00,                   // Limits: Min only
    283                               0x01,                   // Limits: Min
    284                               0x09,                   // Elements section
    285                               0x08,                   // Section size
    286                               0x01,                   // One element segment
    287                               flag,                   // Flag should be 2, or > 2 if invalid
    288                               tblindex,               // Table index must be 0, or > 0 if invalid
    289                               0x41,                   // Init expr: i32.const
    290                               0x00,                   // Init expr: zero (payload)
    291                               0x0b,                   // Init expr: end
    292                               0x00,                   // Extern kind: Function
    293                               0x00]);                 // Zero functions
    294    }
    295 
    296    // Should succeed because this is what an active segment with index looks like
    297    new WebAssembly.Module(makeIt(0x02, 0x00));
    298 
    299    // Should fail because the kind is unknown
    300    assertErrorMessage(() => new WebAssembly.Module(makeIt(0x08, 0x00)),
    301                       WebAssembly.CompileError,
    302                       /invalid elem segment flags field/);
    303 
    304    // Should fail because the table index is invalid
    305    assertErrorMessage(() => new WebAssembly.Module(makeIt(0x02, 0x01)),
    306                       WebAssembly.CompileError,
    307                       /table index out of range/);
    308 }
    309 
    310 // table of externref
    311 {
    312    const myArray = ["yay", "arrays"];
    313    const { set, get } = wasmEvalText(`(module
    314        (table $t 2 externref)
    315 
    316        (func (export "set") (param i32 externref)
    317            (table.set $t (local.get 0) (local.get 1))
    318        )
    319        (func (export "get") (param i32) (result externref)
    320            (table.get $t (local.get 0))
    321        )
    322    )`).exports;
    323    assertEq(get(0), null);
    324    assertEq(get(1), null);
    325    set(0, "hello");
    326    set(1, myArray);
    327    assertEq(get(0), "hello");
    328    assertEq(get(1), myArray);
    329 }
    330 
    331 // table of externref with active element segment
    332 {
    333    const myArray = ["yay", "arrays"];
    334    const { get } = wasmEvalText(`(module
    335        (global (import "test" "g1") externref)
    336        (global (import "test" "g2") externref)
    337        (table $t externref (elem (global.get 0) (global.get 1)))
    338 
    339        (func (export "get") (param i32) (result externref)
    340            (table.get $t (local.get 0))
    341        )
    342    )`, { test: { g1: "hello", g2: myArray } }).exports;
    343    assertEq(get(0), "hello");
    344    assertEq(get(1), myArray);
    345 }
    346 
    347 // passive element segment of externref
    348 {
    349    const myArray = ["yay", "arrays"];
    350    const { get } = wasmEvalText(`(module
    351        (global (import "test" "g1") externref)
    352        (global (import "test" "g2") externref)
    353        (table $t 2 externref)
    354        (elem $e externref (global.get 0) (global.get 1))
    355 
    356        (func $start
    357            (table.init $t $e (i32.const 0) (i32.const 0) (table.size $t))
    358        )
    359        (func (export "get") (param i32) (result externref)
    360            (table.get $t (local.get 0))
    361        )
    362 
    363        (start $start)
    364    )`, { test: { g1: "hello", g2: myArray } }).exports;
    365    assertEq(get(0), "hello");
    366    assertEq(get(1), myArray);
    367 }
    368 
    369 // declared element segment of externref (literally useless but legal!)
    370 {
    371    const myArray = ["yay", "arrays"];
    372    wasmEvalText(`(module
    373        (global (import "test" "g1") externref)
    374        (global (import "test" "g2") externref)
    375        (elem $e declare externref (global.get 0) (global.get 1))
    376    )`, { test: { g1: "hello", g2: myArray } });
    377 }
    378 
    379 // result types are validated in element expressions
    380 {
    381    assertErrorMessage(() => wasmEvalText(`(module
    382        (elem $e externref (ref.func 0))
    383        (func)
    384    )`), WebAssembly.CompileError, /expression has type (funcref|\(ref 0\)) but expected externref/);
    385 }