From 318767745e00fcc83cea153ce3977b583893ea23 Mon Sep 17 00:00:00 2001 From: Thomas Vermeilh Date: Thu, 1 Aug 2024 11:13:37 +0200 Subject: [PATCH] fix panic on malformed xref stream width If the xref stream specifies an entry width of 9, read_u64_from_stream() will try to bitshift the u64 by `<< 8 * 9` which is invalid. Don't accept width that don't fit into a u64. If the xref stream specifies a valid width, but data is exhausted, read_u64_from_stream() will cause an out of bound access and panic. Check if we have enough bytes before reading. Fix the function doc comment, the width is in bytes, not bits. --- pdf/src/parser/parse_xref.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pdf/src/parser/parse_xref.rs b/pdf/src/parser/parse_xref.rs index 16576ec2..de287fdd 100644 --- a/pdf/src/parser/parse_xref.rs +++ b/pdf/src/parser/parse_xref.rs @@ -26,10 +26,10 @@ fn parse_xref_section_from_stream(first_id: u32, mut num_entries: usize, width: let _type = if w0 == 0 { 1 } else { - read_u64_from_stream(w0, data) + read_u64_from_stream(w0, data)? }; - let field1 = read_u64_from_stream(w1, data); - let field2 = read_u64_from_stream(w2, data); + let field1 = read_u64_from_stream(w1, data)?; + let field2 = read_u64_from_stream(w2, data)?; let entry = match _type { @@ -45,8 +45,14 @@ fn parse_xref_section_from_stream(first_id: u32, mut num_entries: usize, width: entries, }) } -/// Helper to read an integer with a certain amount of bits `width` from stream. -fn read_u64_from_stream(width: usize, data: &mut &[u8]) -> u64 { +/// Helper to read an integer with a certain amount of bytes `width` from stream. +fn read_u64_from_stream(width: usize, data: &mut &[u8]) -> Result { + if width > std::mem::size_of::() { + return Err(PdfError::Other { msg: format!("xref stream entry has invalid width {}", width) }); + } + if width > data.len() { + return Err(PdfError::Other { msg: format!("xref stream entry has width {} but only {} bytes left to read", width, data.len()) }); + } let mut result = 0; for i in (0..width).rev() { let base = 8 * i; // (width, 0] @@ -54,7 +60,7 @@ fn read_u64_from_stream(width: usize, data: &mut &[u8]) -> u64 { *data = &data[1..]; // Consume byte result += u64::from(c) << base; } - result + Ok(result) }