asm: scheme_read string buffer overflow (#GP fault on > 272 chars)

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.
This commit is contained in:
russell@unturf.com 2026-06-04 21:09:00 -04:00
parent 6262a24d47
commit 6b03516ecb
No known key found for this signature in database

View file

@ -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