From c0b78a8bd50caf93508bf3fdf6f47a4a83ab5d92 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Mon, 26 Oct 2026 12:27:59 -0400 Subject: [PATCH] Cap UTF-8 text collection and guard mesh lookups in warp validation block_chars now takes at most the remaining text allowance, matching the mixed-segment arm. The mesh-fit check uses guarded lookups for the us, vs and points arrays so a malformed mesh returns false instead of panicking. Generated with Codebuff Co-Authored-By: Codebuff --- crates/affinity/src/text.rs | 8 ++++---- crates/geom/src/warp.rs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/affinity/src/text.rs b/crates/affinity/src/text.rs index 14317eb791..eb52cda436 100644 --- a/crates/affinity/src/text.rs +++ b/crates/affinity/src/text.rs @@ -69,14 +69,14 @@ pub(crate) fn read(r: &mut Reader, id: ObjId, world: Affine) -> Option { /// inline object (page number, anchor) takes one `U+FFFC` before its segment's text. Stops once /// `total` plus these characters would pass the cap. fn block_chars(s: &Stream, glyphs: ObjId, total: usize, placeholders: &mut bool) -> Vec { + // One past the allowance is enough to trip the check in `read`; both arms stop there. + let room = MAX_CHARS.saturating_sub(total).saturating_add(1); match s.str(glyphs, b"Utf8") { - Some(t) => t.chars().collect(), + Some(t) => t.chars().take(room).collect(), None => { // Inline objects (page numbers, anchors) each take one index before the segment text. let mut v = Vec::new(); - // One past the allowance is enough to trip the check below; shared segments - // repeated by reference must not expand beyond it first. - let room = MAX_CHARS.saturating_sub(total).saturating_add(1); + // Shared segments repeated by reference must not expand beyond the allowance first. for seg in s.objs(glyphs, b"Mixd") { if v.len() >= room { break; diff --git a/crates/geom/src/warp.rs b/crates/geom/src/warp.rs index 5a221b8ba8..0ca3c36b98 100644 --- a/crates/geom/src/warp.rs +++ b/crates/geom/src/warp.rs @@ -1111,12 +1111,20 @@ impl Warp { /// One sample row (`j` within patch row `pj`) of [`Self::mesh_is_box_fit`]. fn mesh_row_is_box_fit(&self, m: &BezierMesh, pj: usize, j: usize, pc: usize) -> bool { let nx = m.nx(); - let v = m.vs[pj] + (m.vs[pj + 1] - m.vs[pj]) * j as f64 / 3.0; + let (Some(&v0), Some(&v1)) = (m.vs.get(pj), m.vs.get(pj + 1)) else { + return false; + }; + let v = v0 + (v1 - v0) * j as f64 / 3.0; for pi in 0..pc { + let (Some(&u0), Some(&u1)) = (m.us.get(pi), m.us.get(pi + 1)) else { + return false; + }; for i in 0..4 { - let u = m.us[pi] + (m.us[pi + 1] - m.us[pi]) * i as f64 / 3.0; + let u = u0 + (u1 - u0) * i as f64 / 3.0; let expected = self.box_point(u, v); - let actual = m.points[(3 * pj + j) * nx + 3 * pi + i]; + let Some(&actual) = m.points.get((3 * pj + j) * nx + 3 * pi + i) else { + return false; + }; if !point_fits(expected, actual) { return false; }