fmt: scandigits rejects the i32-overflowing last digit (F-A)

The one-sided guard `v > 214748364` never fired for the last digit:
at v==214748364 a next digit of '8'/'9' made `v*10+digit` overflow
i32 and wrap negative, slipping past the signed args-index bound
check at fmt.ww:703 -> OOB arg read -> SIGSEGV on any format
directive carrying an over-i32 digit run (index, width or precision).

Complete it to the canonical two-part pre-multiply Horner guard
(MAX/10, MAX%10). Hand-rolled in signed i32, not Hare scan_sz's
unsigned post-multiply wrap-check (ref/hare/strconv/stou.ha:60),
which would be signed-overflow UB-class here; noted at the site.

Table-driven subprocess test over all three scandigits call sites,
5 rows x both stages; reverting the guard reproduces exit=139.
This commit is contained in:
2026-06-14 11:46:10 +09:00
parent 33f940e17c
commit 1752305be7
3 changed files with 247 additions and 1 deletions

View File

@@ -478,6 +478,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
$(BIN)/test_memio_vstream_run \
$(BIN)/test_fmt_handle_run \
$(BIN)/test_fmt_mods_run \
$(BIN)/test_fmt_scanoverflow_run \
$(BIN)/test_fmt_compositions_run \
$(BIN)/test_fmt_int_run \
$(BIN)/test_fieldfn_leaf_collide_run \
@@ -2034,6 +2035,13 @@ $(BIN)/test_fmt_mods_run: test/wcc/780_fmt_mods_run.c \
$(LIB)/libwwrt.a | $(BIN)
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/test_fmt_scanoverflow_run: test/wcc/989_fmt_scanoverflow_run.c \
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
lib/fmt/fmt.ww \
$(LIB)/libwwrt.a | $(BIN)
$(CC) $(CFLAGS) -o $@ $<
$(BIN)/test_fmt_compositions_run: test/wcc/781_fmt_compositions_run.c \
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \