Akuli / jou

Yet another programming language
MIT License
12 stars 4 forks source link

Valgrind error on a compiled executable when optimizations are enabled #224

Closed Akuli closed 1 year ago

Akuli commented 1 year ago

content of bug.jou:

from "stdlib/mem.jou" import malloc

struct Token:
    kind: int
    path: byte*
    lineno: int
    lineno2: int
    int_value: int
    long_value: long
    byte_value: byte
    indentation_level: int
    short_string: byte[100]

def foo() -> Token*:
    tokens: Token* = NULL
    tokens = malloc(sizeof(tokens[0]))
    tokens[0] = Token{kind = 1}
    return tokens

def main() -> int:
    foo()
    return 0

Running it under valgrind on Linux:

$ ./jou -o asd -O1 bug.jou
$ valgrind ./asd

Expected result: no valgrind errors except the leak. Actual result: Invalid write error

==20167== Memcheck, a memory error detector
==20167== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==20167== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==20167== Command: ./asd
==20167== 
==20167== Invalid write of size 8
==20167==    at 0x401152: foo (in /home/akuli/jou/asd)
==20167==    by 0x40119F: ??? (in /home/akuli/jou/asd)
==20167==    by 0x4011A5: main (in /home/akuli/jou/asd)
==20167==  Address 0x4b810cc is 140 bytes inside a block of size 144 alloc'd
==20167==    at 0x483877F: malloc (vg_replace_malloc.c:307)
==20167==    by 0x40113A: foo (in /home/akuli/jou/asd)
==20167==    by 0x40119F: ??? (in /home/akuli/jou/asd)
==20167==    by 0x4011A5: main (in /home/akuli/jou/asd)
==20167== 
==20167== 
==20167== HEAP SUMMARY:
==20167==     in use at exit: 144 bytes in 1 blocks
==20167==   total heap usage: 1 allocs, 0 frees, 144 bytes allocated
==20167== 
==20167== LEAK SUMMARY:
==20167==    definitely lost: 144 bytes in 1 blocks
==20167==    indirectly lost: 0 bytes in 0 blocks
==20167==      possibly lost: 0 bytes in 0 blocks
==20167==    still reachable: 0 bytes in 0 blocks
==20167==         suppressed: 0 bytes in 0 blocks
==20167== Rerun with --leak-check=full to see details of leaked memory
==20167== 
==20167== For lists of detected and suppressed errors, rerun with: -s
==20167== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Akuli commented 1 year ago

Here is a minimal reproducer that does not use the Jou compiler. Commands to run it are included as comments. Comments assume this file is saved to log.ll

; /usr/lib/llvm-11/bin/opt -O1 < log.ll > log2.ll && clang-11 log.ll && valgrind -q ./a.out && clang-11 log2.ll && valgrind -q ./a.out
; /usr/lib/llvm-11/bin/llvm-dis < log2.ll

target triple = "x86_64-pc-linux-gnu"

@printf_string = private global [22 x i8] c"Allocated %lld bytes\0A\00"

declare i32 @printf(i8*, ...)
declare { i32, i64, i8 }* @malloc(i64)

define i32 @main() {
  ; Calculate sizeof({ i32, i64, i8 }) and allocate that much heap memory.
  %size = ptrtoint { i32, i64, i8 }* getelementptr ({ i32, i64, i8 }, { i32, i64, i8 }* null, i32 1) to i64
  %heap_ptr = call { i32, i64, i8 }* @malloc(i64 %size)

  ; Print the size we calculated.
  ;%printf_format = getelementptr inbounds [22 x i8], [22 x i8]* @printf_string, i32 0, i32 0
  ;%r = call i32 (i8*, ...) @printf(i8* %printf_format, i64 %size)

  ; Store data into the pointer.
  ; If I change the data from 1,2,3 to 0,0,0 then it works...
  store { i32, i64, i8 } { i32 1, i64 2, i8 3 }, { i32, i64, i8 }* %heap_ptr, align 4

  ret i32 0
}