Functions outside of the event loop segfault on LLVM
jiribenes opened this issue · comments
Continuing from #494:
Description
The following (already quite minimised [!]) program is supposed to:
- read a file
- split it into lines
- for each line, tell me the positions of the first and last occurrences of a given digit in the line
The program somewhat heavily uses strings and I/O, and acts wildly differently between the JS and the LLVM backends:
The buggy program
import io/files
import io/error
import io
def processLine(line: String) = {
println(line)
val numbers: List[Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers.foreach { n =>
val numberStr = n.show
val firstSeen = line.indexOf(numberStr)
val lastSeen = line.lastIndexOf(numberStr)
println("n: " ++ n.show ++ " ('" ++ numberStr ++ "') is seen: first: " ++ firstSeen.show ++ " & last: " ++ lastSeen.show)
}
}
def processAll(lines: String) = {
lines.split("\n").foreach { line => processLine(line) }
}
def main() = {
// this crashes, but if you move it inside the `eventloop`, it works fine!
processAll("kjrqmzv9mmtxhgvsevenhvq7\nfour2tszbgmxpbvninebxns6nineqbqzgjpmpqr")
eventloop(box {
with on[IOError].panic;
with filesystem;
val contents = do readFile("tiny.input")
processAll(contents)
})
}
where tiny.input
is just
kjrqmzv9mmtxhgvsevenhvq7
four2tszbgmxpbvninebxns6nineqbqzgjpmpqr
On the JS backend, everything just works :)
On the LLVM backend, I get a hard crash with exit code 134.
kjrqmzv9mmtxhgvsevenhvq7
n: 1 ('1') is seen: first: None() & last: None()
n: 2 ('2') is seen: first: None() & last: None()
n: 3 ('3') is seen: first: None() & last: None()
n: 4 ('4') is seen: first: None() & last: None()
n: 5 ('5') is seen: first: None() & last: None()
n: 6 ('6') is seen: first: None() & last: None()
n: 7 ('7') is seen: first: Some(23) & last: Some(23)
n: 8 ('8') is seen: first: None() & last: None()
n: 9 ('9') is seen: first: Some(7) & last: Some(7)
four2tszbgmxpbvninebxns6nineqbqzgjpmpqr
n: 1 ('1') is seen: first: None() & last: None()
n: 2 ('2') is seen: first: Some(4) & last: Some(4)
n: 3 ('3') is seen: first: None() & last: None()
n: 4 ('4') is seen: first: None() & last: None()
n: 5 ('5') is seen: first: None() & last: None()
n: 6 ('6') is seen: first: Some(23) & last: Some(23)
n: 7 ('7') is seen: first: None() & last: None()
n: 8 ('8') is seen: first: None() & last: None()
n: 9 ('9') is seen: first: None() & last: None()
[error] Process exited with non-zero exit code 134.
Investigated a bit closer, here's the sanitiser trace:
🔍 Sanitiser output
repro(11106,0x100d18580) malloc: nano zone abandoned due to inability to preallocate reserved vm space.
[...]
=================================================================
==10874==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x619000000080 in thread T0
#0 0x1033592c0 in wrap_free+0x90 (libclang_rt.asan_osx_dynamic.dylib:arm64+0x4d2c0) (BuildId: 7615c595d022355bb91dd63f44086bc832000000200000000100000000000b00)
#1 0x102eed76c in topLevel+0x38 (repro:arm64+0x10000976c) (BuildId: 59fdb92c81573573a0ba6a4222172fd232000000200000000100000000000b00)
#2 0x10305feb4 in uv_run+0x25c (libuv.1.dylib:arm64+0x7eb4) (BuildId: 208160f0746f3fb2a43dfcdb736ed89c32000000200000000100000000000b00)
#3 0x102eefdc0 in k_300+0x1c (repro:arm64+0x10000bdc0) (BuildId: 59fdb92c81573573a0ba6a4222172fd232000000200000000100000000000b00)
#4 0x10325d088 in start+0x204 (dyld:arm64+0x5088) (BuildId: 38ee9fe9b66d30668c5c6ddf0d6944c632000000200000000100000000060c00)
#5 0x2d157ffffffffffc (<unknown module>)
Address 0x619000000080 is a wild pointer inside of access range of size 0x000000000001.
SUMMARY: AddressSanitizer: bad-free (libclang_rt.asan_osx_dynamic.dylib:arm64+0x4d2c0) (BuildId: 7615c595d022355bb91dd63f44086bc832000000200000000100000000000b00) in wrap_free+0x90
==10874==ABORTING
where k_300
is:
define fastcc void @k_300(%Env %env, %Sp %sp) {
entry:
%x_8415p_301 = getelementptr {%Pos}, %Env %env, i64 0, i32 0
%x_8415 = load %Pos, ptr %x_8415p_301
%x_8414 = call %Pos @start_3001()
%x_8413 = call %Pos @stop_3002()
%tmp411540_6285p_302 = getelementptr {%Pos}, %Env %env, i64 0, i32 0
store %Pos %x_8413, ptr %tmp411540_6285p_302
%sp_304 = getelementptr %FrameHeader, %Sp %sp, i64 -1
%retadrp_305 = getelementptr %FrameHeader, %Sp %sp_304, i64 0, i32 0
%f_303 = load %RetAdr, ptr %retadrp_305
tail call fastcc void %f_303(%Env %env, %Sp %sp_304)
ret void
}
# OPT:
define fastcc void @k_300(ptr %env, ptr %sp) {
entry:
%loop.i = tail call ptr @uv_default_loop()
%run_result.i = tail call i32 @uv_run(ptr %loop.i, i32 0)
%loop.i2 = tail call ptr @uv_default_loop()
tail call void @uv_stop(ptr %loop.i2)
tail call void @uv_loop_close(ptr %loop.i2)
%sp_304 = getelementptr %FrameHeader, ptr %sp, i64 -1
tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %env, i8 0, i64 16, i1 false)
%f_303 = load ptr, ptr %sp_304, align 8
tail call fastcc void %f_303(ptr nonnull %env, ptr nonnull %sp_304)
ret void
}
This case is called 3. in #494, see more details there #494 (comment)