Compiling the following program with 1.38.37 fastcomp using
emcc -Os -s BINARYEN_IGNORE_IMPLICIT_TRAPS=1 test.cpp -o test.js
causes the program to misbehave and crash with
RuntimeError: memory access out of bounds
The optimizer removes the conditional surrounding the access to dontAccess and merges it all into one block.
Here's the minimal repro code:
int len = 0;
unsigned* dontAccess = 0;
int main() {
if (len == 0 || dontAccess[len - 1]) {
len += 1;
}
return 0;
}
Omitting BINARYEN_IGNORE_IMPLICIT_TRAPS or changing the optimization level to e.g. O3 fixes it.
Here's the WAST output of the working version. Notice the nested ifs.
func (result i32)
(local i32)
i32.const 1024
i32.load offset=0 align=4
tee_local 0
if <== CHECK IF LEN IS NON-ZERO AND AVOID ACCESSING THE ARRAY
get_local 0
i32.const -1
i32.add
i32.const 2
i32.shl
i32.load offset=0 align=4
i32.eqz
if
i32.const 0
return
end
end
i32.const 1024
get_local 0
i32.const 1
i32.add
i32.store offset=0 align=4
i32.const 0
end
Here's the WAST output from the failing case. The ifs have been flattened and both memory accesses always happen unconditionally (to len and to the start of dontAccess) causing an invalid memory read.
func (result i32)
(local i32)
i32.const 1024
i32.load offset=0 align=4
tee_local 0
i32.eqz <== CHECK IF LEN IS ZERO
get_local 0
i32.const -1
i32.add
i32.const 2
i32.shl
i32.load offset=0 align=4 <== THIS CRASHES
i32.or <== NAIVELY OR THE RESULTS OF BOTH COMPARISONS
if
i32.const 1024
get_local 0
i32.const 1
i32.add
i32.store offset=0 align=4
end
i32.const 0
end
This does work with the latest upstream Clang 9 based Emscripten, but I think it's still worth investigating since the upstream doesn't seem to be read for prime time yet.
Compiling the following program with 1.38.37 fastcomp using
emcc -Os -s BINARYEN_IGNORE_IMPLICIT_TRAPS=1 test.cpp -o test.jscauses the program to misbehave and crash with
RuntimeError: memory access out of boundsThe optimizer removes the conditional surrounding the access to dontAccess and merges it all into one block.
Here's the minimal repro code:
Omitting BINARYEN_IGNORE_IMPLICIT_TRAPS or changing the optimization level to e.g. O3 fixes it.
Here's the WAST output of the working version. Notice the nested ifs.
Here's the WAST output from the failing case. The ifs have been flattened and both memory accesses always happen unconditionally (to len and to the start of dontAccess) causing an invalid memory read.
This does work with the latest upstream Clang 9 based Emscripten, but I think it's still worth investigating since the upstream doesn't seem to be read for prime time yet.