From 2a130abf8229cf41385d422b449d0cf71deb5ff7 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 15 Oct 2026 08:52:17 +0200 Subject: [PATCH] Refuse header/footer Bates numbers that would overflow u64 (#2253) Found while testing crates/edit/src/lib.rs:445 Co-authored-by: Claude Haiku 5.5 Co-authored-by: Brandon Thomas --- contributors/people.toml | 4 ++++ crates/edit/src/lib.rs | 12 ++++++++++-- crates/edit/src/tests.rs | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/contributors/people.toml b/contributors/people.toml index e8249f065c..268df23a2f 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -136,3 +136,7 @@ real_name = "Alexander Alber" [people.mtlcl410] display_name = "mtlcl410" sync_github_name = false + +[people.Silvertree2010] +display_name = "Netter" +sync_github_name = false diff --git a/crates/edit/src/lib.rs b/crates/edit/src/lib.rs index 01334e9f16..515f17d8da 100644 --- a/crates/edit/src/lib.rs +++ b/crates/edit/src/lib.rs @@ -450,18 +450,26 @@ pub fn add_header_footer(doc: &mut Document, pages: &[usize], hf: &HeaderFooter, u32::MAX ))); } + // The same goes for Bates numbers: the last selected page must still fit in a u64. + let bates0 = hf.text.iter().find_map(|t| bates_start(t)).unwrap_or(1); + if bates0.checked_add(pages.len().saturating_sub(1) as u64).is_none() { + return Err(EditError::Invalid(format!( + "the Bates start number {bates0} leaves no room for {} pages (Bates numbers stop at {})", + pages.len(), + u64::MAX + ))); + } if replace { remove_marks(doc, pages, MarkKind::HeaderFooter)?; } let all = page_list(doc); let count = all.len(); - let bates0 = hf.text.iter().find_map(|t| bates_start(t)).unwrap_or(1); for (k, &i) in pages.iter().enumerate() { let page = &all[i]; let (w, h) = page.display_size(doc); let number = u32::try_from(u64::from(hf.start_number) + k as u64).map_err(|_| EditError::Invalid("the header page numbers are too large".into()))?; - let bates = bates0 + k as u64; + let bates = bates0.checked_add(k as u64).ok_or_else(|| EditError::Invalid("the Bates numbers are too large".into()))?; let size = hf.font_size; let [top, bottom, left, right] = hf.margins; let mut body: Vec = format!("BT\n/PCHelv {} Tf\n{}\n", n(size), rgb(hf.color)).into_bytes(); diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index 2fea0d6646..e4c49d7411 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -115,6 +115,23 @@ fn header_start_number_that_would_wrap_is_refused_without_drawing() { assert_eq!(marks_present(&doc).len(), 1); } +#[test] +fn header_bates_start_that_would_overflow_is_refused_without_drawing() { + // A Bates start of u64::MAX has no room for a second page. The run must be refused up front + // instead of overflowing (a panic in debug builds, a wrap to 0 in release builds). + let mut doc = fixture(); + let hf = HeaderFooter { + text: [String::new(), String::new(), "<>".into(), String::new(), String::new(), String::new()], + ..HeaderFooter::default() + }; + let err = add_header_footer(&mut doc, &[0, 1], &hf, false, &cx()).unwrap_err(); + assert!(err.to_string().contains("Bates"), "{err}"); + assert!(marks_present(&doc).is_empty(), "nothing is drawn when the run is refused"); + // A single page still fits the start number itself. + add_header_footer(&mut doc, &[0], &hf, false, &cx()).unwrap(); + assert_eq!(marks_present(&doc).len(), 1); +} + #[test] fn replace_and_remove_restore_the_original_content() { let mut doc = fixture();