From c0239310145bdec6fd4ef7771eb917a24c08cdea Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Fri, 29 May 2026 05:57:19 +0200 Subject: [PATCH] fix: pad source read buffer for tree-sitter lookahead (ASan over-read) read_file allocated size+1, but tree-sitter's lexer peeks a few bytes past the final UTF-8 char (ts_lexer_goto), reading past the buffer end. Under ASan this is a heap-buffer-overflow (intermittently flagged in the incremental suite on macOS); in production it is a benign over-read of adjacent heap. Over-allocate by 16 bytes and zero the tail so the lookahead stays in-bounds. --- src/pipeline/pass_definitions.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pipeline/pass_definitions.c b/src/pipeline/pass_definitions.c index ac147ae54..779758c84 100644 --- a/src/pipeline/pass_definitions.c +++ b/src/pipeline/pass_definitions.c @@ -45,7 +45,12 @@ static char *read_file(const char *path, int *out_len) { return NULL; } - char *buf = malloc(size + SKIP_ONE); + /* +16 padding: tree-sitter's lexer peeks a few bytes past the final UTF-8 + * character when computing lookahead, reading beyond the logical end. + * Over-allocate and zero the tail so that read stays in-bounds (ASan + * flags it as a heap-buffer-overflow otherwise; harmless but real UB). */ + enum { CBM_TS_LOOKAHEAD_PAD = 16 }; + char *buf = malloc((size_t)size + CBM_TS_LOOKAHEAD_PAD); if (!buf) { (void)fclose(f); return NULL; @@ -57,7 +62,7 @@ static char *read_file(const char *path, int *out_len) { if (nread > (size_t)size) { nread = (size_t)size; } - buf[nread] = '\0'; + memset(buf + nread, 0, CBM_TS_LOOKAHEAD_PAD); *out_len = (int)nread; return buf; }