tor-browser

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

errors.js (10141B)


      1 load(libdir + "wasm-binary.js");
      2 
      3 const Module = WebAssembly.Module;
      4 const Instance = WebAssembly.Instance;
      5 const CompileError = WebAssembly.CompileError;
      6 const RuntimeError = WebAssembly.RuntimeError;
      7 
      8 function getWasmFunctionIndex(line) {
      9    return Number(line.match(/^wasm-function\[(\d*)\]$/)[1]);
     10 }
     11 
     12 function getWasmBytecode(column) {
     13    return parseInt(column.match(/^0x([0-9a-f]*)$/)[1], 16);
     14 }
     15 
     16 function parseStack(stack) {
     17    var frames = stack.split('\n');
     18    assertEq(frames[frames.length-1], "");
     19    frames.length--;
     20    return frames.map(frame => {
     21        var res = frame.match(/^(.*)@(.*):(.*):(.*)$/);
     22        assertEq(res !== null, true);
     23        return {name: res[1], url: res[2], line: res[3], column: res[4]};
     24    });
     25 }
     26 
     27 function testExn(opcode, binary, type, msg, exn) {
     28    assertEq(exn instanceof type, true);
     29    assertEq(msg.test(exn.message), true);
     30 
     31    var stack = parseStack(exn.stack);
     32    assertEq(stack.length > 1, true);
     33    var innermost = stack[0];
     34    var funcIndex = getWasmFunctionIndex(innermost.line);
     35    var bytecode = getWasmBytecode(innermost.column);
     36    assertEq(exn.lineNumber, bytecode);
     37    assertEq(exn.columnNumber, 1);
     38    assertEq(binary[bytecode], opcode);
     39 
     40    return {stack, binary};
     41 }
     42 
     43 function test(opcode, text, type, msg) {
     44    var binary = new Uint8Array(wasmTextToBinary(text));
     45    var exn;
     46    try {
     47        new Instance(new Module(binary));
     48    } catch (e) {
     49        exn = e;
     50    }
     51 
     52    return testExn(opcode, binary, type, msg, exn);
     53 }
     54 
     55 function testAccess(opcode, text, width, type, msg) {
     56    var binary = new Uint8Array(wasmTextToBinary(text));
     57    var instance = new Instance(new Module(binary));
     58    for (var base of [64 * 1024, 2 * 64 * 1024, Math.pow(2, 30), Math.pow(2, 31), Math.pow(2, 32) - 1]) {
     59        for (var sub = 0; sub < width; sub++) {
     60            var ptr = base - sub;
     61            let exn = null;
     62            try {
     63                instance.exports[''](ptr);
     64            } catch (e) {
     65                exn = e;
     66            }
     67            testExn(opcode, binary, type, msg, exn);
     68        }
     69    }
     70 }
     71 
     72 function testLoad(opcode, optext, width, type, msg) {
     73    var text = `(module (memory 1) (func (export "") (param i32) (drop (${optext} (local.get 0)))))`;
     74    testAccess(opcode, text, width, type, msg);
     75 }
     76 
     77 function testStore(opcode, optext, consttext, width, type, msg) {
     78    var text = `(module (memory 1) (func (export "") (param i32) (${optext} (local.get 0) (${consttext}.const 0))))`;
     79    testAccess(opcode, text, width, type, msg);
     80 }
     81 
     82 test(UnreachableCode, '(module (func unreachable) (start 0))', RuntimeError, /unreachable executed/);
     83 test(I32DivSCode, '(module (func (drop (i32.div_s (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     84 test(I32DivSCode, '(module (func (drop (i32.div_s (i32.const -2147483648) (i32.const -1)))) (start 0))', RuntimeError, /integer overflow/);
     85 test(I32DivUCode, '(module (func (drop (i32.div_u (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     86 test(I32RemSCode, '(module (func (drop (i32.rem_s (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     87 test(I32RemUCode, '(module (func (drop (i32.rem_u (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     88 test(I64DivSCode, '(module (func (drop (i64.div_s (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     89 test(I64DivSCode, '(module (func (drop (i64.div_s (i64.const -9223372036854775808) (i64.const -1)))) (start 0))', RuntimeError, /integer overflow/);
     90 test(I64DivUCode, '(module (func (drop (i64.div_u (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     91 test(I64RemSCode, '(module (func (drop (i64.rem_s (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     92 test(I64RemUCode, '(module (func (drop (i64.rem_u (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/);
     93 test(I32TruncSF32Code, '(module (func (drop (i32.trunc_f32_s (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
     94 test(I32TruncSF64Code, '(module (func (drop (i32.trunc_f64_s (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
     95 test(I32TruncUF32Code, '(module (func (drop (i32.trunc_f32_u (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
     96 test(I32TruncUF64Code, '(module (func (drop (i32.trunc_f64_u (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
     97 test(I64TruncSF32Code, '(module (func (drop (i64.trunc_f32_s (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
     98 test(I64TruncSF64Code, '(module (func (drop (i64.trunc_f64_s (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
     99 test(I64TruncUF32Code, '(module (func (drop (i64.trunc_f32_u (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
    100 test(I64TruncUF64Code, '(module (func (drop (i64.trunc_f64_u (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/);
    101 test(I32TruncSF32Code, '(module (func (drop (i32.trunc_f32_s (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    102 test(I32TruncSF64Code, '(module (func (drop (i32.trunc_f64_s (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    103 test(I32TruncUF32Code, '(module (func (drop (i32.trunc_f32_u (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    104 test(I32TruncUF64Code, '(module (func (drop (i32.trunc_f64_u (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    105 test(I64TruncSF32Code, '(module (func (drop (i64.trunc_f32_s (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    106 test(I64TruncSF64Code, '(module (func (drop (i64.trunc_f64_s (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    107 test(I64TruncUF32Code, '(module (func (drop (i64.trunc_f32_u (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    108 test(I64TruncUF64Code, '(module (func (drop (i64.trunc_f64_u (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/);
    109 test(CallIndirectCode, '(module (table 1 funcref) (func (call_indirect (type 0) (i32.const 0))) (start 0))', RuntimeError, /indirect call to null/);
    110 test(CallIndirectCode, '(module (table 1 funcref) (func (call_indirect (type 0) (i32.const 1))) (start 0))', RuntimeError, /index out of bounds/);
    111 test(CallIndirectCode, '(module (table funcref (elem $blah)) (func (call_indirect (type 0) (i32.const 0))) (func $blah (param i32)) (start 0))', RuntimeError, /indirect call signature mismatch/);
    112 testLoad(I32Load8S, 'i32.load8_s', 1, RuntimeError, /index out of bounds/);
    113 testLoad(I32Load8U, 'i32.load8_u', 1, RuntimeError, /index out of bounds/);
    114 testLoad(I32Load16S, 'i32.load16_s', 2, RuntimeError, /index out of bounds/);
    115 testLoad(I32Load16U, 'i32.load16_u', 2, RuntimeError, /index out of bounds/);
    116 testLoad(I64Load8S, 'i64.load8_s', 1, RuntimeError, /index out of bounds/);
    117 testLoad(I64Load8U, 'i64.load8_u', 1, RuntimeError, /index out of bounds/);
    118 testLoad(I64Load16S, 'i64.load16_s', 2, RuntimeError, /index out of bounds/);
    119 testLoad(I64Load16U, 'i64.load16_u', 2, RuntimeError, /index out of bounds/);
    120 testLoad(I64Load32S, 'i64.load32_s', 4, RuntimeError, /index out of bounds/);
    121 testLoad(I64Load32U, 'i64.load32_u', 4, RuntimeError, /index out of bounds/);
    122 testLoad(I32Load, 'i32.load', 4, RuntimeError, /index out of bounds/);
    123 testLoad(I64Load, 'i64.load', 8, RuntimeError, /index out of bounds/);
    124 testLoad(F32Load, 'f32.load', 4, RuntimeError, /index out of bounds/);
    125 testLoad(F64Load, 'f64.load', 8, RuntimeError, /index out of bounds/);
    126 testStore(I32Store8, 'i32.store8', 'i32', 1, RuntimeError, /index out of bounds/);
    127 testStore(I32Store16, 'i32.store16', 'i32', 2, RuntimeError, /index out of bounds/);
    128 testStore(I64Store8, 'i64.store8', 'i64', 1, RuntimeError, /index out of bounds/);
    129 testStore(I64Store16, 'i64.store16', 'i64', 2, RuntimeError, /index out of bounds/);
    130 testStore(I64Store32, 'i64.store32', 'i64', 4, RuntimeError, /index out of bounds/);
    131 testStore(I32Store, 'i32.store', 'i32', 4, RuntimeError, /index out of bounds/);
    132 testStore(I64Store, 'i64.store', 'i64', 8, RuntimeError, /index out of bounds/);
    133 testStore(F32Store, 'f32.store', 'f32', 4, RuntimeError, /index out of bounds/);
    134 testStore(F64Store, 'f64.store', 'f64', 8, RuntimeError, /index out of bounds/);
    135 
    136 // Stack overflow isn't really a trap or part of the formally-specified
    137 // semantics of call so use the same InternalError as JS and use the bytecode
    138 // offset of the function body (which happens to start with the number of
    139 // local entries).
    140 test(4 /* = num locals */, '(module (func (local i32 i64 f32 f64) (call 0)) (start 0))', InternalError, /too much recursion/);
    141 
    142 // Test whole callstack.
    143 var {stack, binary} = test(UnreachableCode, `(module
    144    (type $v2v (func))
    145    (func $a unreachable)
    146    (func $b call $a)
    147    (func $c call $b)
    148    (table funcref (elem $c))
    149    (func $d (call_indirect (type $v2v) (i32.const 0)))
    150    (func $e call $d)
    151    (start $e)
    152 )`, RuntimeError, /unreachable executed/);
    153 const N = 5;
    154 assertEq(stack.length > N, true);
    155 assertEq(getWasmFunctionIndex(stack[0].line), 0);
    156 var lastLine = stack[0].line;
    157 for (var i = 1; i < N; i++) {
    158    assertEq(getWasmFunctionIndex(stack[i].line), i);
    159    assertEq(stack[i].line > lastLine, true);
    160    lastLine = stack[i].line;
    161    assertEq(binary[getWasmBytecode(stack[i].column)], i == 3 ? CallIndirectCode : CallCode);
    162 }
    163 
    164 function testCompileError(opcode, text) {
    165    var binary = new Uint8Array(wasmTextToBinary(text));
    166    var exn;
    167    try {
    168        new Instance(new Module(binary));
    169    } catch (e) {
    170        exn = e;
    171    }
    172 
    173    assertEq(exn instanceof CompileError, true);
    174    var offset = Number(exn.message.match(/at offset (\d*)/)[1]);
    175    assertEq(binary[offset], opcode);
    176 }
    177 
    178 testCompileError(CallCode, '(module (func $f (param i32)) (func $g call $f))');
    179 testCompileError(I32AddCode, '(module (func (i32.add (i32.const 1) (f32.const 1))))');
    180 testCompileError(EndCode, '(module (func (block (result i32))))');