From 6b03516ecbb994c674357fd34f8878c252073c3e Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Thu, 4 Jun 2026 21:09:00 -0400 Subject: [PATCH] asm: scheme_read string buffer overflow (#GP fault on > 272 chars) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reader's .sr_string used a fixed 256-byte stack buffer with no bounds check. Strings longer than ~272 bytes (256 + saved registers) corrupted the saved return address and produced a general protection fault on ret. Reproducer: (display (string-length "AAAA...")) ; > 272 A's → #GP at .sr_string ret Fix: - bump stack buffer 256 → 4096 (one page) - add bounds check (cmpq $4080) before every char write - on overflow exit cleanly via new die_str_overflow rather than smashing %rip Discovered while diagnosing ecdsa task #34: lumbda asm tier crashed when loading ecdsa/lumbda/mod-arith.lsp because one mod-mul! docstring is 955 bytes. Post-fix, mod-arith.lsp loads clean and ecdsa test-mod-arith.lsp passes 33/33 on asm-full; upstream asm test.sh stays 158/158. --- asm/lumbda.s | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/asm/lumbda.s b/asm/lumbda.s index e8a3d43..c0e68ca 100644 --- a/asm/lumbda.s +++ b/asm/lumbda.s @@ -517,6 +517,8 @@ err_rng_short: .ascii "Error: random-state!: expected list of 8 integers\n" .equ err_rng_short_len, . - err_rng_short err_rng_urandom: .ascii "Error: random-seed-from-os!: /dev/urandom unavailable\n" .equ err_rng_urandom_len, . - err_rng_urandom +err_str_overflow: .ascii "Error: scheme_read: string literal exceeds 4080-byte buffer\n" +.equ err_str_overflow_len, . - err_str_overflow s_dev_urandom: .asciz "/dev/urandom" # Print strings @@ -1619,6 +1621,16 @@ die_oom: movq $1, %rdi syscall +die_str_overflow: + movq $SYS_WRITE, %rax + movq $2, %rdi + leaq err_str_overflow(%rip), %rsi + movq $err_str_overflow_len, %rdx + syscall + movq $SYS_EXIT, %rax + movq $1, %rdi + syscall + # ============================================================ # Value constructors # ============================================================ @@ -2284,10 +2296,19 @@ list_to_vector_reader: ret .sr_string: + # Reads a "..." string literal into a 4080-byte stack buffer, then + # heap-allocates the final string. Buffer bumped from 256B (which + # smashed the saved return address on strings > 272 chars and + # produced a #GP fault — see ecdsa lumbda task #34, 2026-06-04). + # 4080 chosen to keep the frame size under 4096 (one page) while + # bounding via cmpq below. If a string literal exceeds the cap, + # exit cleanly via die_str_overflow rather than corrupting %rip. call read_char # eat opening " - subq $256, %rsp + subq $4096, %rsp xorq %rbx, %rbx # length .sr_str_loop: + cmpq $4080, %rbx + jge die_str_overflow call read_char cmpq $-1, %rax je .sr_str_end @@ -2342,7 +2363,7 @@ list_to_vector_reader: incq %rcx jmp .sr_str_copy .sr_str_done: - addq $256, %rsp + addq $4096, %rsp orq $TAG_STRING, %rax popq %r12 popq %rbx