prev()'s walk-back decremented offs (i32) past 0 to -1 and returned `more`; a subsequent next() then passed the signed `-1 < len` guard and read d.src[-1] — a silent OOB decode of a garbage rune (no runtime bounds net). Hare's decoder.offs is `size`: the underflow wraps to SIZE_MAX so every `offs < len` guard exits safely (next returns more, not a rune). Change offs to size and spell prev's loop as the Hare-form `offs < len` guard; index sites take an i32 temp (ww's slice index is i32 and `[...]` reads ':' as the slice separator). No-runtime-net residual: remaining() would silently build a ptr-1/len+1 OOB view when called in the post-`more` state; guard it with a loud abort (caller contract: don't call after `more`). The offs type ripples into strings.ww's iterator<->decoder bridge (move/slice) — cast at the four sites, safe on the rune-return path where offs is in range. utf8/strings embed into all five selfhost combined.ww snapshots plus the smoke.combined.ww test amalgamation; all regen'd. utf8test gains prev_more_then_next_no_oob pinning the closed OOB.
1.6 MiB
1.6 MiB