carp-lang / Carp

A statically typed lisp, without a GC, for real-time applications.
Apache License 2.0
5.47k stars 173 forks source link

Memory management in nested lambdas is not working properly #1416

Open TimDeve opened 2 years ago

TimDeve commented 2 years ago

Allocating inside a lambda an using the value in a nested lambda results in double free:

(Debug.sanitize-addresses)

(defn main []
  ((fn []
    (let [s @"Ok"]
      ((fn [] (println* s)))))))
Ok
=================================================================
==10746==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0:
    #0 0x4c0a47 in free (/home/tim/dev/sandbox/match/out/Untitled+0x4c0a47)
    #1 0x4fafb4 in String_delete /home/tim/dev/other/Carp/core/carp_string.h:20:5
    #2 0x556109 in _Lambda__Lambda_NAKED_LAMBDA_23_env_20_env_ty_delete /home/tim/dev/sandbox/match/out/main.c:11227:5
    #3 0x53fd4a in Function_delete__void /home/tim/dev/sandbox/match/out/main.c:8050:7
    #4 0x555ea7 in _Lambda_NAKED_LAMBDA_23_env /home/tim/dev/sandbox/match/out/main.c:11208:9
    #5 0x5565b9 in main /home/tim/dev/sandbox/match/out/main.c:11261:61
    #6 0x7fe9aa2abe09 in __libc_start_main /builddir/glibc-2.32/csu/../csu/libc-start.c:314:16
    #7 0x41c739 in _start /builddir/glibc-2.32/csu/../sysdeps/x86_64/start.S:120

0x602000000010 is located 0 bytes inside of 3-byte region [0x602000000010,0x602000000013)
freed by thread T0 here:
    #0 0x4c0a47 in free (/home/tim/dev/sandbox/match/out/Untitled+0x4c0a47)
    #1 0x4fafb4 in String_delete /home/tim/dev/other/Carp/core/carp_string.h:20:5
    #2 0x54e02b in StringCopy_str /home/tim/dev/sandbox/match/out/main.c:10990:5
    #3 0x55600e in _Lambda__Lambda_NAKED_LAMBDA_23_env_20_env /home/tim/dev/sandbox/match/out/main.c:11213:18
    #4 0x555dce in _Lambda_NAKED_LAMBDA_23_env /home/tim/dev/sandbox/match/out/main.c:11207:19
    #5 0x5565b9 in main /home/tim/dev/sandbox/match/out/main.c:11261:61
    #6 0x7fe9aa2abe09 in __libc_start_main /builddir/glibc-2.32/csu/../csu/libc-start.c:314:16

previously allocated by thread T0 here:
    #0 0x4c0d3f in malloc (/home/tim/dev/sandbox/match/out/Untitled+0x4c0d3f)
    #1 0x4fb2ba in String_copy /home/tim/dev/other/Carp/core/carp_string.h:67:18
    #2 0x555c01 in _Lambda_NAKED_LAMBDA_23_env /home/tim/dev/sandbox/match/out/main.c:11196:21
    #3 0x5565b9 in main /home/tim/dev/sandbox/match/out/main.c:11261:61
    #4 0x7fe9aa2abe09 in __libc_start_main /builddir/glibc-2.32/csu/../csu/libc-start.c:314:16

SUMMARY: AddressSanitizer: double-free (/home/tim/dev/sandbox/match/out/Untitled+0x4c0a47) in free
==10746==ABORTING
[RUNTIME ERROR] 'out/Untitled' exited with return value 1.

With Matches

Using match confuses the compiler

(defn main []
  ((fn []
    (let [r (Just @"Ok")]
      (match r
        (Just s) ((fn [] (println* s)))
        _ ())))))

Compiler output:

carp: Too many variables with the same name in set: [(ProperDel String.delete  "s"),(FakeDel "s")]
CallStack (from HasCallStack):
  error, called at src/Memory.hs:512:24 in CarpHask-0.5.4.0-JdeTIwgYM2SBeF4dr02mnA:Memory

While using match-ref results in a badly emitted C source:

(defn main []
  ((fn []
    (let [r (Just @"Ok")]
      (match-ref &r
        (Just s) ((fn [] (println* s)))
        _ ())))))

Output:

out/main.c:11292:18: error: use of undeclared identifier 's'
    _35_env->s = s;
                 ^
1 error generated.
carp: callCommand: clang  -o out/Untitled -I /home/tim/dev/other/Carp/core/  -fPIC -g -std=c99 -D_DEFAULT_SOURCE -Wall -Werror -Wno-unused-variable -Wno-self-assign -lm out/main.c (exit 1): failed
scolsen commented 2 years ago

copying over some thoughts from chat:

this is similar to issue #597 but what's probably happening here is s gets too different types in the match clause and body, which results in it being assigned two different deleters. We can probably handle this similarly to the way shadows were handled in #597 and use a new state for the body scope that we pop afterwards.

For the double-free issue, the problem is sort of the opposite phenomenon where a variable that should be treated as the same is being treated as distinct. This might be because of the state popping happening in let bindings now, but I'm not sure.