summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/passive-segs-partial-mem.js
blob: 5e9351ef8a759e1ca1b6930e538ace7eeba1571e (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
let conf = getBuildConfiguration();
if (conf.debug &&
    (conf["arm-simulator"] || conf["arm64-simulator"] ||
     conf["mips32-simulator"] || conf["mips64-simulator"]))
{
    // Will timeout, so just quit early.
    quit(0);
}

// Sundry test cases for the "partial write" bounds checking semantics.

const PAGESIZE = 65536;

// memory.fill: out of bounds, should not perform writes
//
// Arithmetic overflow of memory offset + len should not affect the behavior, we
// should still fill up to the limit.

function mem_fill(min, max, shared, backup, write=backup*2) {
    if (shared == "shared" && !sharedMemoryEnabled())
        return;
    let ins = wasmEvalText(
        `(module
           (memory (export "mem") ${min} ${max} ${shared})
           (func (export "run") (param $offs i32) (param $val i32) (param $len i32)
             (memory.fill (local.get $offs) (local.get $val) (local.get $len))))`);
    // A fill past the end should throw *and* not have filled all the way up to
    // the end
    let offs = min*PAGESIZE - backup;
    let val = 37;
    assertErrorMessage(() => ins.exports.run(offs, val, write),
                       WebAssembly.RuntimeError,
                       /index out of bounds/);
    let v = new Uint8Array(ins.exports.mem.buffer);
    for (let i=0; i < backup; i++)
        assertEq(v[offs+i], 0);
    for (let i=0; i < offs; i++)
        assertEq(v[i], 0);
}

mem_fill(1, 1, "", 256);
mem_fill(1, 1, "", 257);
mem_fill(1, 1, "", 257, 0xFFFFFFFF); // offs + len overflows 32-bit

mem_fill(2, 4, "shared", 256);
mem_fill(2, 4, "shared", 257);
mem_fill(2, 4, "shared", 257, 0xFFFFFFFF); // offs + len overflows 32-bit

// memory.init: out of bounds of the memory or the segment, and should not perform
// the operation at all.
//
// Arithmetic overflow of memoffset + len or of bufferoffset + len should not
// affect the behavior.

// Note, the length of the data segment is 16.
const mem_init_len = 16;

function mem_init(min, max, shared, backup, write) {
    if (shared == "shared" && !sharedMemoryEnabled())
        return;
    let ins = wasmEvalText(
        `(module
           (memory (export "mem") ${min} ${max} ${shared})
           (data "\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42")
           (func (export "run") (param $offs i32) (param $len i32)
             (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len))))`);
    // A fill writing past the end of the memory should throw *and* not have filled
    // all the way up to the end.
    //
    // A fill reading past the end of the segment should throw *and* not have filled
    // memory with as much data as was available.
    let offs = min*PAGESIZE - backup;
    assertErrorMessage(() => ins.exports.run(offs, write),
                       WebAssembly.RuntimeError,
                       /index out of bounds/);
    let v = new Uint8Array(ins.exports.mem.buffer);
    for (let i=0; i < min; i++)
        assertEq(v[offs + i], 0);
}

// We exceed the bounds of the memory but not of the data segment
mem_init(1, 1, "", Math.floor(mem_init_len/2), mem_init_len);
mem_init(1, 1, "", Math.floor(mem_init_len/2)+1, mem_init_len);
mem_init(2, 4, "shared", Math.floor(mem_init_len/2), mem_init_len);
mem_init(2, 4, "shared", Math.floor(mem_init_len/2)+1, mem_init_len);

// We exceed the bounds of the data segment but not the memory
mem_init(1, 1, "", mem_init_len*4, mem_init_len*2-2);
mem_init(1, 1, "", mem_init_len*4-1, mem_init_len*2-1);
mem_init(2, 4, "shared", mem_init_len*4, mem_init_len*2-2);
mem_init(2, 4, "shared", mem_init_len*4-1, mem_init_len*2-1);

// We arithmetically overflow the memory limit but not the segment limit
mem_init(1, "", "", Math.floor(mem_init_len/2), 0xFFFFFF00);

// We arithmetically overflow the segment limit but not the memory limit
mem_init(1, "", "", PAGESIZE, 0xFFFFFFFC);

// memory.copy: out of bounds of the memory for the source or target, and should
// not perform 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

function mem_copy(min, max, shared, srcOffs, targetOffs, len) {
    if (shared == "shared" && !sharedMemoryEnabled())
        return;
    let ins = wasmEvalText(
        `(module
           (memory (export "mem") ${min} ${max} ${shared})
           (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32)
             (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))))`);

    let v = new Uint8Array(ins.exports.mem.buffer);

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

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

    for (var i=0, s=0; i < v.length; i++ ) {
        if (i >= srcOffs && i < srcLim) {
            assertEq(v[i], (s++) & 0xFF);
            continue;
        }
        assertEq(v[i], 0);
    }
}

// OOB target address, nonoverlapping
mem_copy(1, 1, "", 0, PAGESIZE-20, 40);
mem_copy(1, 1, "", 0, PAGESIZE-21, 39);
mem_copy(2, 4, "shared", 0, 2*PAGESIZE-20, 40);
mem_copy(2, 4, "shared", 0, 2*PAGESIZE-21, 39);

// OOB source address, nonoverlapping
mem_copy(1, 1, "", PAGESIZE-20, 0, 40);
mem_copy(1, 1, "", PAGESIZE-21, 0, 39);
mem_copy(2, 4, "shared", 2*PAGESIZE-20, 0, 40);
mem_copy(2, 4, "shared", 2*PAGESIZE-21, 0, 39);

// OOB target address, overlapping, src < target
mem_copy(1, 1, "", PAGESIZE-50, PAGESIZE-20, 40);

// OOB source address, overlapping, target < src
mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-50, 40);

// OOB both, overlapping, including target == src
mem_copy(1, 1, "", PAGESIZE-30, PAGESIZE-20, 40);
mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-30, 40);
mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-20, 40);

// Arithmetic overflow on source address.
mem_copy(1, "", "", PAGESIZE-20, 0, 0xFFFFF000);

// Arithmetic overflow on target adddress is an overlapping case.
mem_copy(1, 1, "", PAGESIZE-0x1000, PAGESIZE-20, 0xFFFFFF00);