lib/encoding/utf8: decoder offs i32->size, closing prev/next OOB (#70)
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.
This commit is contained in:
@@ -586,17 +586,20 @@ export fn riter(src: str) iterator = {
|
||||
fn move(forward: bool, it: *iterator) (rune | utf8.done) = {
|
||||
let d: utf8.decoder;
|
||||
d.src = it.src;
|
||||
d.offs = it.offs;
|
||||
// utf8.decoder.offs is `size` (#70); the iterator carries i32. The
|
||||
// rune-return path keeps offs in [0, len), so the narrowing cast back
|
||||
// is safe.
|
||||
d.offs = it.offs: size;
|
||||
if (forward) {
|
||||
match (utf8.next(&d)) {
|
||||
case let r: rune => { it.offs = d.offs; return r; };
|
||||
case let r: rune => { it.offs = d.offs: i32; return r; };
|
||||
case let dn: utf8.done => return dn;
|
||||
case let m: utf8.more => abort("strings.move: invalid UTF-8");
|
||||
case let e: utf8.invalid => abort("strings.move: invalid UTF-8");
|
||||
};
|
||||
} else {
|
||||
match (utf8.prev(&d)) {
|
||||
case let r: rune => { it.offs = d.offs; return r; };
|
||||
case let r: rune => { it.offs = d.offs: i32; return r; };
|
||||
case let dn: utf8.done => return dn;
|
||||
case let m: utf8.more => abort("strings.move: invalid UTF-8");
|
||||
case let e: utf8.invalid => abort("strings.move: invalid UTF-8");
|
||||
@@ -639,10 +642,10 @@ export fn iterstr(it: *iterator) str = {
|
||||
export fn slice(begin: *iterator, end: *iterator) str = {
|
||||
let b: utf8.decoder;
|
||||
b.src = begin.src;
|
||||
b.offs = begin.offs;
|
||||
b.offs = begin.offs: size; // decoder.offs is size (#70)
|
||||
let e: utf8.decoder;
|
||||
e.src = end.src;
|
||||
e.offs = end.offs;
|
||||
e.offs = end.offs: size;
|
||||
return frombytes(utf8.slice(&b, &e));
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user