effekt-lang / effekt

A research language with effect handlers and lightweight effect polymorphism

Home Page:https://effekt-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

  1. read a file
  2. split it into lines
  3. 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)