From f2cd13bb7676592ac405b3e97991ae849acd31ba Mon Sep 17 00:00:00 2001 From: Mohamed MAACHE Date: Sun, 5 Jul 2026 11:33:59 +0200 Subject: [PATCH] fix(xlsb): consume unmatched workbook-global record bodies read_workbook's record loop left unmatched workbook-global records unconsumed, so the next read_type() call would misread the leftover body as the next record id. Depending on the body size this either dropped every sheet silently or panicked while decoding BrtBundleSh from an empty buffer. Consume the body of any unhandled record via fill_buffer, same as the other matched arms already do. Fixes #666 --- src/xlsb/mod.rs | 6 +++++- tests/issue_666_lost_sheets.xlsb | Bin 0 -> 1748 bytes tests/issue_666_panic.xlsb | Bin 0 -> 1760 bytes tests/test.rs | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/issue_666_lost_sheets.xlsb create mode 100644 tests/issue_666_panic.xlsb diff --git a/src/xlsb/mod.rs b/src/xlsb/mod.rs index d5d93a59..cab7ab54 100644 --- a/src/xlsb/mod.rs +++ b/src/xlsb/mod.rs @@ -340,7 +340,11 @@ impl Xlsb { } } 0x0090 => break, // BrtEndBundleShs - _ => (), + _ => { + // Consume the body of any unmatched workbook-global record + // to avoid reading its trailing bytes in the next read. + let _ = iter.fill_buffer(&mut buf)?; + } } buf.clear(); } diff --git a/tests/issue_666_lost_sheets.xlsb b/tests/issue_666_lost_sheets.xlsb new file mode 100644 index 0000000000000000000000000000000000000000..5ce3891c85bd0e4f9b318d68282793edcd1f68f7 GIT binary patch literal 1748 zcmds1J#W)M7`|(oE*+>u2gFj#>W1P=Kl^{SjK-I;*#8&*JyK6~=klNA_ z38;_|AAhAoHzs}o3riRN1n=2N9A5)LOo+2y+xPLl&wJ1N+;w(K%j-zBJ0CBfoNip4 zyut`6yAETI@5Yg2k=)-KB}_cEhG9_OftPW}xbWl1HfvVZ+-cO)c>`u5VVjCAwVK(Yb)}sf3G+IM|&QLfDglUYE zQCPM~lO*sRDj^GU7`YlsWsG2fXSzmsej;u`8YY>G(DkDQ0HLU`p~C`8c#KI494H@I zA$54%ifA`rE_5IY#J>;paNlJ{hjMw3hLA292IN`H2dT3l>8GZG{3+_xSpkrg??$Ik zyz{-}TNw(cY!!;PuUI3B0WrU54`;dg%i6?Wf({A z*WLi3|5kgA(Pz9l$w{TjQ5(6ahF0MJ5%Lg&M?xQRKgFmhluzHc&sJbh%5K7X*m|ZF zrLhTt|C?^|^D~vqbf|D17YV1ix^=MutSP&Zu{Sr+F-Gt38Z0QZw-}vabfQXvm)1+a zzIrEKN2&PcT*P_+{rKkr&LPCiWuQCiCn0zIv2^8J=+3&9a_7s^@06v-(OJ#oY?h}< WT)8|uyJaPKREDPoyBxtFbDPC)8V<zfc`5T`fnf>78W)JW}feoT+R&yF(G_CAwVK(Yb)}sf3G+IM|&QLfDglUY6 zQCPM~lO*sRDxnH;7`YlsWsG1!Wa=Y4KM}W~43o@7==#wDfKXJ}&|!fkJjSF20hAwE zA$54%ifA`rE=(W`D$YnvY+$&sO03l-&e>pdr(Z z(%Xd0|4l>rS(?gcx>Puui$v61-MZWW)|B1I*qfW^IYw{s8aNcj8;s5{I#w;gOY5ay zU&E8AQ7Xham$IHfzY6jM7m#A+V$cKiQ;`RLTe@;a^k7{}dGPnr@06wI(OJ#&RLj#M Wu3V&@-LeuqD#O!)uRH}`gnj~(ec = wb("issue_666_lost_sheets.xlsb"); + assert_eq!(xlsb.sheet_names(), vec!["Sheet1".to_string()]); +} + +#[test] +fn test_xlsb_unmatched_workbook_record_does_not_panic() { + // The unmatched record's body size varint starts with `0x9C`, which the + // desynced reader misreads as `BrtBundleSh` and decodes from an empty buffer. + let xlsb: Xlsb<_> = wb("issue_666_panic.xlsb"); + assert_eq!(xlsb.sheet_names(), vec!["Sheet1".to_string()]); +} + #[test] fn too_small_xls() { let path = test_path("too_small.xls");