Summary
ASAN reports use-after-free in scratchspace
Metadata
- Id: ee079d4edb202a3ece54c1706f4a3ce98106ebd1
- Trac id:
- Type: defect
- Reporter: felix
- Owner:
- Cc:
- Status: new
- Component: core libraries
- Estimated difficulty: hard
- Resolution:
- Priority: major
- Milestone:
- Version: 6.0.1pre1
- Changetime: 2026-09-11 21:18:16 UTC
- Created: 2026-09-11 21:18:16 UTC
- Keywords:
Description
Reported by Nat Page:
I have a project I've been porting to C6 that I build with AddressSanitizer, and I've found a use-after free relating to the new functionality for passing structs directly.
This is the simplest repro I've been able to come up with so far. It's incredibly fragile to small changes (e.g. adding a call to display on the wrong line), and always immediately preceded by garbage collector activity, outputting something like:
[GC] (old) scratchspace: start=0x0000000000000000, limit=0x0000000000000000 [GC] (new) scratchspace: start=0x00007d74dfbe1480, limit=0x00007d74dfbe1c80 0123456789101112131415161718192021222324[GC] (old) scratchspace: start=0x00007d74dfbe1480, limit=0x00007d74dfbe1c80 [GC] (new) scratchspace: start=0x00007d74dfbe1e80, limit=0x00007d74dfbe2680 ================================================================= ==243608==ERROR: AddressSanitizer: heap-use-after-free on address 0x7d74dfbe1498 at pc 0x55aae0314bc4 bp 0x7fffa3a64260 sp 0x7fffa3a64258 READ of size 4 at 0x7d74dfbe1498 thread T0
... that is, a read into the old scratch space.
Using a pretty recent GCC:
$ gcc --version gcc (GCC) 15.3.0 Copyright (C) 2025 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Reproduces on the released 6.0.0 version:
$ csc -version CHICKEN (c)2000-2007 Felix L. Winkelmann, (c)2008 The CHICKEN Team Version 6.0.0 (rev 2e30c07e) linux-unix-gnu-x86-64 [ 64bit dload ptables ]
Also reproduces on HEAD at the time of writing:
$ csc -version
____, CHICKEN / ^ > (c) 2008 The CHICKEN Team /| ____ (c) 2000-2007 Felix L. Winkelmann ||/ \\ linux-unix-gnu-x86-64 [ 64bit dload ptables ] || || \\____/ . 0 . 1pre1 (rev d6442142)
Build with: $ csc asan-repro.scm -C -fsanitize-trap -C -fsanitize=address -L -fsanitize=address
- ; File
- asan-repro.scm
(import (chicken foreign) (chicken gc))
#>
struct foo {
int bar;
};
<#
(define make-foo
(foreign-lambda* (struct "foo") ((int bar))
"struct foo v = { .bar = bar };"
"C_return(v);" ) )
- ; I haven't been able to get it to trigger without a function like this, but
- ; that could just be bad luck on my part. For a while I had a two-argument
- ; version but it turns out that a one-argument version works fine.
(define foo-id
(foreign-lambda* (struct "foo")
(((struct "foo") v))
"C_return(v);" ) )
(define foo-bar
(foreign-lambda* int
(((struct "foo") v))
"C_return(v.bar);" ) )
(set-gc-report! 'all)
(let ((a (make-foo 0)))
(do ([k 0 (+ k 1)])
((= k 100))
(foo-id (make-foo k))
(foo-bar a)
;; FWIW, this also triggers the issue (comment out the previous two lines,
;; and uncomment these):
;; (make-foo k)
;; (foo-bar (foo-id a))
;; On the other hand, removing the call to foo-id entirely but upping the
;; number of iterations doesn't seem to help.
;; this next line can be removed without breaking the repro, but it makes
;; it easier to count the number of iterations (I get 25, as seen above).
(display k) ) )