diff --git a/src/xls.rs b/src/xls.rs index c788bd44..2f39e12a 100644 --- a/src/xls.rs +++ b/src/xls.rs @@ -1532,6 +1532,15 @@ fn parse_formula( // PtgExp: array/shared formula, ignore debug!("ignoring PtgExp array/shared formula"); stack.push(formula.len()); + // Ensure PtgExp carries a 4-byte operand for the row/column + // of the array or shared formula it points at. + if rgce.len() < 4 { + return Err(XlsError::Len { + expected: 4, + found: rgce.len(), + typ: "PtgExp", + }); + } rgce = &rgce[4..]; } 0x03..=0x11 => { diff --git a/tests/ptgexp-truncated-operand.xls b/tests/ptgexp-truncated-operand.xls new file mode 100644 index 00000000..ee9129af Binary files /dev/null and b/tests/ptgexp-truncated-operand.xls differ diff --git a/tests/test.rs b/tests/test.rs index 6f4b4b83..cd8cbad4 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -3245,6 +3245,18 @@ fn biff5_formula_ptg_ref_643() { assert_eq!(formulas.get_value((3, 1)), Some(&"$A$1".to_string())); } +#[test] +fn test_xls_truncated_ptgexp() { + // Test for xls file with a truncated PtgExp token (array/shared formula) + // with 2 bytes instead of 4. + let mut excel: Xls<_> = wb("ptgexp-truncated-operand.xls"); + let range = excel.worksheet_range("Tab 1").unwrap(); + assert_eq!(range.get_size(), (23, 10)); + + let formulas = excel.worksheet_formula("Tab 1").unwrap(); + assert_eq!(formulas.used_cells().count(), 22); +} + #[test] fn biff5_rich_text_string() { // This file uses RSTRING records, apparently produced by ABBYY FineReader.