From c2e0fcdcf176e943101a23ebc5351371de8e64f8 Mon Sep 17 00:00:00 2001 From: rsheeter Date: Mon, 19 Aug 2024 22:06:11 -0700 Subject: [PATCH] Fix fuzzer bug with cmap4 --- font-test-data/src/lib.rs | 1 + read-fonts/src/tables/cmap.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/font-test-data/src/lib.rs b/font-test-data/src/lib.rs index d677477fc..407ecff28 100644 --- a/font-test-data/src/lib.rs +++ b/font-test-data/src/lib.rs @@ -1,5 +1,6 @@ //! test data shared between various fontations crates. +pub mod cmap; pub mod gdef; pub mod gpos; pub mod gsub; diff --git a/read-fonts/src/tables/cmap.rs b/read-fonts/src/tables/cmap.rs index 496fbb32b..6c9159a00 100644 --- a/read-fonts/src/tables/cmap.rs +++ b/read-fonts/src/tables/cmap.rs @@ -164,7 +164,19 @@ impl<'a> Iterator for Cmap4Iter<'a> { return Some((codepoint, glyph_id)); } else { self.cur_range_ix += 1; - self.cur_range = self.subtable.code_range(self.cur_range_ix)?; + // self.cur_range = self.subtable.code_range(self.cur_range_ix)?; + // eprintln!("Range {:?}", self.cur_range); + let next_range = self.subtable.code_range(self.cur_range_ix)?; + // Groups should be in order and non-overlapping so make sure + // that the start code of next group is at least current_end + 1. + // Also avoid start sliding backwards if we see data where end < start by taking the max + // of next.end and curr.end as the new end. + // This prevents timeout and bizarre results in the face of numerous overlapping ranges + // https://github.com/googlefonts/fontations/issues/1100 + // cmap4 ranges are u16 so no need to stress about values past char::MAX + self.cur_range = next_range.start.max(self.cur_range.end) + ..next_range.end.max(self.cur_range.end); + eprintln!("Range {:?}", self.cur_range); self.cur_start_code = self.cur_range.start as u16; } } @@ -749,4 +761,20 @@ mod tests { _ => None, }) } + + /// + /// + /// Note that this doesn't demonstrate the timeout, merely that we've eliminated the underlying + /// enthusiasm for non-ascending ranges that enabled it + #[test] + fn cmap4_bad_data() { + let buf = font_test_data::cmap::repetitive_cmap4(); + let cmap4 = Cmap4::read(FontData::new(buf.as_slice())).unwrap(); + + // we should have unique, ascending codepoints, not duplicates and overlaps + assert_eq!( + (6..=64).collect::>(), + cmap4.iter().map(|(cp, _)| cp).collect::>() + ); + } }