Skip to content

[Bug] Heap-use-after-free in shorthash_for_name caused by grow_source_buffer reallocating lexer buffer #385

@oneafter

Description

@oneafter

Description

We discovered a Heap-use-after-free vulnerability in the Lily interpreter. The crash occurs when shorthash_for_name attempts to access a string pointer pointing into the lexer's source buffer, which has just been freed/moved by a realloc in grow_source_buffer.

This is a classic pointer invalidation issue where a pointer to the source code buffer is held while an operation triggers the buffer to grow and move.

Environment

  • OS: Linux x86_64
  • Complier: Clang
  • Build Configuration: Release mode with ASan enabled.

Vulnerability Details

  • Target: Lily (jcowgar/lily)
  • Vulnerability Type: CWE-416: Use After Free
  • Function: shorthash_for_name
  • Location: src/lily_symtab.c:521 (Usage) / src/lily_lexer.c:124 (Free via realloc)
  • Root Cause Analysis:
  1. The parser is processing an expression (expr_word) and likely holds a pointer to the current token's text, which resides inside the Lexer's source_buffer.
  2. The logic triggers resolve_module (as seen in the "Freed by" stack trace), which calls lily_next_token to read more input.
  3. lily_next_token calls read_line. If the line is long or the buffer is full, grow_source_buffer is called.
  4. grow_source_buffer calls realloc. If realloc moves the memory block, the original buffer is freed.
  5. The control flow returns to expr_word, which proceeds to call lily_find_var -> shorthash_for_name using the original (now dangling) pointer into the old buffer.

Reproduce

  1. Build lily with Release optimization and ASAN enabled.
  2. Run with the crashing file:
./build/lily repro.lily
ASAN report
==87409==ERROR: AddressSanitizer: heap-use-after-free on address 0x50c000000100 at pc 0x5623382cd8cc bp 0x7ffcfa29d800 sp 0x7ffcfa29d7f8
READ of size 1 at 0x50c000000100 thread T0
    #0 0x5623382cd8cb in shorthash_for_name /src/lily/src/lily_symtab.c:521:10
    #1 0x5623382cd8cb in lily_find_var /src/lily/src/lily_symtab.c:596:26
    #2 0x562338275bc3 in find_existing_sym /src/lily/src/lily_parser.c:648:26
    #3 0x562338275bc3 in expr_word /src/lily/src/lily_parser.c:2745:19
    #4 0x56233828214f in expression_raw /src/lily/src/lily_parser.c:3242:9
    #5 0x56233828214f in keyword_var /src/lily/src/lily_parser.c:3643:9
    #6 0x562338262efb in parser_loop /src/lily/src/lily_parser.c:5946:17
    #7 0x562338260be9 in lily_parse_content /src/lily/src/lily_parser.c:6562:9
    #8 0x5623381d137e in main /src/lily/run/lily.c:103:18
    #9 0x7fecc3f041c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #10 0x7fecc3f0428a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #11 0x5623380ef624 in _start (/src/lily/build_afl/lily+0x4f624) (BuildId: ec5f784cf13dad836217a07e33a2c98d8b7f8bb4)

0x50c000000100 is located 0 bytes inside of 128-byte region [0x50c000000100,0x50c000000180)
freed by thread T0 here:
    #0 0x56233818f870 in realloc (/src/lily/build_afl/lily+0xef870) (BuildId: ec5f784cf13dad836217a07e33a2c98d8b7f8bb4)
    #1 0x5623381d35e5 in lily_realloc /src/lily/src/lily_alloc.c:14:20
    #2 0x562338238447 in grow_source_buffer /src/lily/src/lily_lexer.c:124:21
    #3 0x562338238447 in read_file_line /src/lily/src/lily_lexer.c:188:9
    #4 0x562338238447 in read_line /src/lily/src/lily_lexer.c:265:16
    #5 0x562338239bf8 in lily_next_token /src/lily/src/lily_lexer.c:1405:17
    #6 0x562338275a83 in resolve_module /src/lily/src/lily_parser.c:1740:9
    #7 0x562338275a83 in expr_word /src/lily/src/lily_parser.c:2744:17
    #8 0x56233828214f in expression_raw /src/lily/src/lily_parser.c:3242:9
    #9 0x56233828214f in keyword_var /src/lily/src/lily_parser.c:3643:9
    #10 0x562338262efb in parser_loop /src/lily/src/lily_parser.c:5946:17
    #11 0x562338260be9 in lily_parse_content /src/lily/src/lily_parser.c:6562:9
    #12 0x5623381d137e in main /src/lily/run/lily.c:103:18
    #13 0x7fecc3f041c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #14 0x7fecc3f0428a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #15 0x5623380ef624 in _start (/src/lily/build_afl/lily+0x4f624) (BuildId: ec5f784cf13dad836217a07e33a2c98d8b7f8bb4)

previously allocated by thread T0 here:
    #0 0x56233818f453 in malloc (/src/lily/build_afl/lily+0xef453) (BuildId: ec5f784cf13dad836217a07e33a2c98d8b7f8bb4)
    #1 0x5623381d3533 in lily_malloc /src/lily/src/lily_alloc.c:5:20
    #2 0x562338233bd2 in lily_new_lex_state /src/lily/src/lily_lexer.c:32:18
    #3 0x5623382538cf in lily_new_state /src/lily/src/lily_parser.c:190:19
    #4 0x5623381d130f in main /src/lily/run/lily.c:90:25
    #5 0x7fecc3f041c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #6 0x7fecc3f0428a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #7 0x5623380ef624 in _start (/src/lily/build_afl/lily+0x4f624) (BuildId: ec5f784cf13dad836217a07e33a2c98d8b7f8bb4)

SUMMARY: AddressSanitizer: heap-use-after-free /src/lily/src/lily_symtab.c:521:10 in shorthash_for_name
Shadow bytes around the buggy address:
  0x50bffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x50bfffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x50bfffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x50c000000000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x50c000000080: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
=>0x50c000000100:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x50c000000180: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x50c000000200: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x50c000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50c000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50c000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==87409==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions