summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/passive-segs-partial-table.js
blob: 2d2e8fcf72d181516ec2cb2b759c7285d007a136 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Sundry test cases for the "partial write" bounds checking semantics.

// table.init: out of bounds of the table or the element segment, and should
// not perform the operation at all.
//
// Arithmetic overflow of tableoffset + len or of segmentoffset + len should not
// affect the behavior.

// Note, the length of the element segment is 16.
const tbl_init_len = 16;

function tbl_init(min, max, backup, write, segoffs=0) {
    let ins = wasmEvalText(
        `(module
           (table (export "tbl") ${min} ${max} funcref)
           (elem func $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15)
           (func $f0 (export "f0"))
           (func $f1 (export "f1"))
           (func $f2 (export "f2"))
           (func $f3 (export "f3"))
           (func $f4 (export "f4"))
           (func $f5 (export "f5"))
           (func $f6 (export "f6"))
           (func $f7 (export "f7"))
           (func $f8 (export "f8"))
           (func $f9 (export "f9"))
           (func $f10 (export "f10"))
           (func $f11 (export "f11"))
           (func $f12 (export "f12"))
           (func $f13 (export "f13"))
           (func $f14 (export "f14"))
           (func $f15 (export "f15"))
           (func (export "run") (param $offs i32) (param $len i32)
             (table.init 0 (local.get $offs) (i32.const ${segoffs}) (local.get $len))))`);
    // A fill writing past the end of the table should throw *and* have filled
    // all the way up to the end.
    //
    // A fill reading past the end of the segment should throw *and* have filled
    // table with as much data as was available.
    let offs = min - backup;
    assertErrorMessage(() => ins.exports.run(offs, write),
                       WebAssembly.RuntimeError,
                       /index out of bounds/);
    let tbl = ins.exports.tbl;
    for (let i=0; i < min; i++) {
        assertEq(tbl.get(i), null);
    }
}

// We exceed the bounds of the table but not of the element segment
tbl_init(tbl_init_len*2, tbl_init_len*4, Math.floor(tbl_init_len/2), tbl_init_len);
tbl_init(tbl_init_len*2, tbl_init_len*4, Math.floor(tbl_init_len/2)-1, tbl_init_len);

// We exceed the bounds of the element segment but not the table
tbl_init(tbl_init_len*10, tbl_init_len*20, tbl_init_len*4, tbl_init_len*2);
tbl_init(tbl_init_len*10, tbl_init_len*20, tbl_init_len*4-1, tbl_init_len*2-1);

// We arithmetically overflow the table limit but not the segment limit
tbl_init(tbl_init_len*4, tbl_init_len*4, tbl_init_len, 0xFFFFFFF0);

// We arithmetically overflow the segment limit but not the table limit
tbl_init(tbl_init_len, tbl_init_len, tbl_init_len, 0xFFFFFFFC, Math.floor(tbl_init_len/2));

// table.copy: out of bounds of the table for the source or target, and should
// perform the operation at all.  Major cases:
//
// - non-overlapping regions
// - overlapping regions with src > dest
// - overlapping regions with src == dest
// - overlapping regions with src < dest
// - arithmetic overflow on src addresses
// - arithmetic overflow on target addresses
//
// for each of those,
//
// - src address oob
// - target address oob
// - both oob
//
// Note we do not test the multi-table case here because that is part of the
// reftypes proposal; tests are in the gc/ subdirectory.

const tbl_copy_len = 16;

function tbl_copy(min, max, srcOffs, targetOffs, len) {
    let ins = wasmEvalText(
        `(module
           (table (export "tbl") ${min} ${max} funcref)
           (func $f0 (export "f0"))
           (func $f1 (export "f1"))
           (func $f2 (export "f2"))
           (func $f3 (export "f3"))
           (func $f4 (export "f4"))
           (func $f5 (export "f5"))
           (func $f6 (export "f6"))
           (func $f7 (export "f7"))
           (func $f8 (export "f8"))
           (func $f9 (export "f9"))
           (func $f10 (export "f10"))
           (func $f11 (export "f11"))
           (func $f12 (export "f12"))
           (func $f13 (export "f13"))
           (func $f14 (export "f14"))
           (func $f15 (export "f15"))
           (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32)
             (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))))`);

    let tbl = ins.exports.tbl;

    let copyDown = srcOffs < targetOffs;
    let targetAvail = tbl.length - targetOffs;
    let srcAvail = tbl.length - srcOffs;
    let srcLim = srcOffs + Math.min(len, targetAvail, srcAvail);

    for (let i=srcOffs, j=0; i < srcLim; i++, j++)
        tbl.set(i, ins.exports["f" + j]);
    assertErrorMessage(() => ins.exports.run(targetOffs, srcOffs, len),
                       WebAssembly.RuntimeError,
                       /index out of bounds/);

    for (var i=0, s=0; i < tbl.length; i++ ) {
        if (i >= srcOffs && i < srcLim) {
            assertEq(tbl.get(i), ins.exports["f" + (s++)]);
            continue;
        }
        assertEq(tbl.get(i), null);
    }
}

// OOB target address, nonoverlapping
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, 0, Math.floor(1.5*tbl_copy_len), tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, 0, Math.floor(1.5*tbl_copy_len)-1, tbl_copy_len-1);

// OOB source address, nonoverlapping
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), 0, tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len)-1, 0, tbl_copy_len-1);

// OOB target address, overlapping, src < target
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len-5, Math.floor(1.5*tbl_copy_len), tbl_copy_len);

// OOB source address, overlapping, target < src
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), tbl_copy_len-5, tbl_copy_len);

// OOB both, overlapping, including src == target
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len+5, Math.floor(1.5*tbl_copy_len), tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, Math.floor(1.5*tbl_copy_len), tbl_copy_len+5, tbl_copy_len);
tbl_copy(tbl_copy_len*2, tbl_copy_len*4, tbl_copy_len+5, tbl_copy_len+5, tbl_copy_len);

// Arithmetic overflow on source address.
tbl_copy(tbl_copy_len*8, tbl_copy_len*8, tbl_copy_len*7, 0, 0xFFFFFFE0);

// Arithmetic overflow on target adddress is an overlapping case.
tbl_copy(tbl_copy_len*8, tbl_copy_len*8, 0, tbl_copy_len*7, 0xFFFFFFE0);