Skip to content

[Bug] Heap-buffer-overflow in peekChar parsing malformed quotes #1217

@oneafter

Description

@oneafter

Description

We discovered a Heap-buffer-overflow vulnerability in Wren. The crash occurs in the compiler's lexer (peekChar) when parsing a source file containing a malformed sequence of quotes (likely triggering raw string parsing).

The ASAN report indicates a READ violation of size 1, occurring immediately after the allocated source buffer.

Environment

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

Vulnerability Details

  • Target: Wren (wren-lang)
  • Vulnerability Type: CWE-125: Out-of-bounds Read
  • Function: peekChar (called by readRawString)
  • Location: src/vm/wren_compiler.c:641
  • Root Cause Analysis: The crash happens when the lexer attempts to parse a raw string literal (triple quotes """).
  1. The PoC ends with a sequence of many quotes: nt(""""""""".
  2. nextToken calls readRawString to handle the string literal.
  3. Inside readRawString, the parser consumes characters looking for the closing delimiter.
  4. It appears the loop logic inside readRawString (or its usage of peekChar) fails to detect the end of the source buffer (EOF/Null terminator) correctly when faced with incomplete or excessive quotes, causing it to read one byte past the end of the heap-allocated source string.

Reproduce

  1. Build wren and harness with Release optimization and ASAN enabled.
harness.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wren.h"

void writeFn(WrenVM* vm, const char* text) {
   
}

void errorFn(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message) {
   
}

int main(int argc, char** argv) {
    if (argc < 2) return 1;
    
    FILE* f = fopen(argv[1], "rb");
    if (!f) return 1;
    
    fseek(f, 0, SEEK_END);
    long length = ftell(f);
    fseek(f, 0, SEEK_SET);
    
    char* buffer = (char*)malloc(length + 1);
    if (!buffer) {
        fclose(f);
        return 1;
    }
    
    if (fread(buffer, 1, length, f) != (size_t)length) {
        free(buffer);
        fclose(f);
        return 1;
    }
    buffer[length] = '\0';
    fclose(f);

    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = writeFn;
    config.errorFn = errorFn;

    WrenVM* vm = wrenNewVM(&config);

    WrenInterpretResult result = wrenInterpret(vm, "main", buffer);

    wrenFreeVM(vm);
    free(buffer);

    return 0;
}
  1. Run with the crashing file:
./bin/harness repro
ASAN report
==71585==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x504000000038 at pc 0x55ef7240e1b4 bp 0x7ffd70a28ab0 sp 0x7ffd70a28aa8
READ of size 1 at 0x504000000038 thread T0
    #0 0x55ef7240e1b3 in peekChar /src/wren/projects/make/../../src/vm/wren_compiler.c:641:10
    #1 0x55ef7240e1b3 in readRawString /src/wren/projects/make/../../src/vm/wren_compiler.c:911:15
    #2 0x55ef7240e1b3 in nextToken /src/wren/projects/make/../../src/vm/wren_compiler.c:1186:11
    #3 0x55ef72408325 in consume /src/wren/projects/make/../../src/vm/wren_compiler.c:1269:3
    #4 0x55ef72408325 in wrenCompile /src/wren/projects/make/../../src/vm/wren_compiler.c:3820:9
    #5 0x55ef723f4aa3 in compileInModule /src/wren/projects/make/../../src/vm/wren_vm.c:484:15
    #6 0x55ef723f3f26 in wrenCompileSource /src/wren/projects/make/../../src/vm/wren_vm.c:1538:25
    #7 0x55ef723f3f26 in wrenInterpret /src/wren/projects/make/../../src/vm/wren_vm.c:1517:25
    #8 0x55ef723e8ca5 in main /src/wren/fuzz_wren.c:51:34
    #9 0x7fc0075061c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #10 0x7fc00750628a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #11 0x55ef723075c4 in _start (/src/wren/bin/fuzz_wren+0x365c4) (BuildId: 5d78be029a4b6a34067ee0d0f65b83b8780504cc)

0x504000000038 is located 0 bytes after 40-byte region [0x504000000010,0x504000000038)
allocated by thread T0 here:
    #0 0x55ef723a73f3 in malloc (/src/wren/bin/fuzz_wren+0xd63f3) (BuildId: 5d78be029a4b6a34067ee0d0f65b83b8780504cc)
    #1 0x55ef723e8bb5 in main /src/wren/fuzz_wren.c:26:27
    #2 0x7fc0075061c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #3 0x7fc00750628a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #4 0x55ef723075c4 in _start (/src/wren/bin/fuzz_wren+0x365c4) (BuildId: 5d78be029a4b6a34067ee0d0f65b83b8780504cc)

SUMMARY: AddressSanitizer: heap-buffer-overflow /src/wren/projects/make/../../src/vm/wren_compiler.c:641:10 in peekChar
Shadow bytes around the buggy address:
  0x503ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x503ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x503ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x503fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x503fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x504000000000: fa fa 00 00 00 00 00[fa]fa fa 00 00 00 00 00 fa
  0x504000000080: fa fa 00 00 00 00 07 fa fa fa 00 00 00 00 07 fa
  0x504000000100: fa fa 00 00 00 00 02 fa fa fa 00 00 00 00 06 fa
  0x504000000180: fa fa 00 00 00 00 06 fa fa fa 00 00 00 00 06 fa
  0x504000000200: fa fa 00 00 00 00 00 01 fa fa 00 00 00 00 05 fa
  0x504000000280: fa fa 00 00 00 00 06 fa fa fa 00 00 00 00 06 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
==71585==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions