Repository: J-F-Liu/lopdf (language: rust)

Issue:
Freeze when loading file
code
```
fn check_file(file_path: &str) {
    let Ok(content) = fs::read(file_path) else {
        return;
    };
    let cursor = Cursor::new(content);
    match Document::load_from(cursor) {
        Ok(mut document) => {
            let pages = document.get_pages();

            let mut doc_clone = document.clone();
            doc_clone.decompress();

            for (i, _) in pages.iter().enumerate() {
                let page_number = (i + 1) as u32;
                let _text = document.extract_text(&[page_number]);
            }

            let _ = document.save_to(&mut Cursor::new(Vec::new()));
        }
        Err(err) => {
            eprintln!("Error reading PDF contents: {}", err)
        }
    }
}
```

freeze  here

```
0x000055555563b91d in core::num::{impl#32}::from_str_radix () at library/core/src/num/mod.rs:1533                       
warning: 1533	library/core/src/num/mod.rs: No such file or directory
(gdb) backtrace
#0  0x000055555563b91d in core::num::{impl#32}::from_str_radix () at library/core/src/num/mod.rs:1533
#1  0x00005555555c21f0 in lopdf::parser::hex_digit::{closure#0}::{closure#0} () at src/nom_parser.rs:213
#2  core::option::Option<&str>::and_then<&str, u8, lopdf::parser::hex_digit::{closure#0}::{closure_env#0}> (self=<error reading variable: Cannot access memory at address 0x0>)
    at /rustc/7120fdac7a6e55a5e4b606256042890b36067052/library/core/src/option.rs:1450
#3  lopdf::parser::hex_digit::{closure#0} (c=...) at src/nom_parser.rs:213
#4  nom::combinator::map_opt::{closure#0}<&[u8], &[u8], u8, (), nom::bytes::complete::take::{closure_env#0}<usize, &[u8], ()>, lopdf::parser::hex_digit::{closure_env#0}> (input=...)
    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/combinator/mod.rs:153
#5  lopdf::parser::hex_digit (input=...) at src/nom_parser.rs:212
#6  core::ops::function::FnMut::call_mut<fn(&[u8]) -> core::result::Result<(&[u8], u8), nom::internal::Err<()>>, (&[u8])> ()
    at /rustc/7120fdac7a6e55a5e4b606256042890b36067052/library/core/src/ops/function.rs:166
#7  nom::internal::{impl#12}::parse<&[u8], u8, (), fn(&[u8]) -> core::result::Result<(&[u8], u8), nom::internal::Err<()>>> (self=<optimized out>, i=...)
    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/internal.rs:325
#8  nom::sequence::preceded::{closure#0}<&[u8], (), u8, (), fn(&[u8]) -> core::result::Result<(&[u8], ()), nom::internal::Err<()>>, fn(&[u8]) -> core::result::Result<(&[u8], u8), nom::internal::Err<()>>> (input=...) at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/sequence/mod.rs:73
#9  nom::internal::{impl#12}::parse<&[u8], u8, (), nom::sequence::preceded::{closure_env#0}<&[u8], (), u8, (), fn(&[u8]) -> core::result::Result<(&[u8], ()), nom::internal::Err<()>>, fn(&[u8]) -> core::result::Result<(&[u8], u8), nom::internal::Err<()>>>> (i=..., self=<optimized out>)
    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/internal.rs:325
#10 nom::multi::fold_many0::{closure#0}<&[u8], u8, (), nom::sequence::preceded::{closure_env#0}<&[u8], (), u8, (), fn(&[u8]) -> core::result::Result<(&[u8], ()), nom::internal::Err<()>>, fn(&[u8]) -> core::result::Result<(&[u8], u8), nom::internal::Err<()>>>, lopdf::parser::hexadecimal_string::{closure_env#1}, lopdf::parser::hexadecimal_string::{closure_env#0}, (alloc::vec::Vec<u8, alloc::alloc::Global>, bool)> (i=...) at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/multi/mod.rs:708


    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/combinator/mod.rs:79
#61 nom::internal::{impl#12}::parse<&[u8], core::result::Result<(lopdf::xref::Xref, lopdf::object::Dictionary), lopdf::error::Error>, (), nom::combinator::map::{closure_env#0}<&[u8], (lopdf::xref::Xref, lopdf::object::Dictionary), core::result::Result<(lopdf::xref::Xref, lopdf::object::Dictionary), lopdf::error::Error>, (), nom::sequence::pair::{closure_env#0}<&[u8], lopdf::xref::Xref, lopdf::object::Dictionary, (), fn(&[u8]) -> core::result::Result<(&[u8], lopdf::xref::Xref), nom::internal::Err<()>>, fn(&[u8]) -> core::result::Result<(&[u8], lopdf::object::Dictionary), nom::internal::Err<()>>>, lopdf::parser::xref_and_trailer::{closure_env#0}>> (self=0x7fffffffcad0, i=...)
    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/internal.rs:325
#62 nom::branch::{impl#1}::choice<&[u8], core::result::Result<(lopdf::xref::Xref, lopdf::object::Dictionary), lopdf::error::Error>, (), nom::combinator::map::{closure_env#0}<&[u8], (lopdf::xref::Xref, lopdf::object::Dictionary), core::result::Result<(lopdf::xref::Xref, lopdf::object::Dictionary), lopdf::error::Error>, (), nom::sequence::pair::{closure_env#0}<&[u8], lopdf::xref::Xref, lopdf::object::Dictionary, (), fn(&[u8]) -> core::result::Result<(&[u8], lopdf::xref::Xref), nom::internal::Err<()>>, fn(&[u8]) -> core::result::Result<(&[u8], lopdf::object::Dictionary), nom::internal::Err<()>>>, lopdf::parser::xref_and_trailer::{closure_env#0}>, lopdf::parser::xref_and_trailer::{closure_env#1}> (self=0x7fffffffcad0, input=...)
    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/branch/mod.rs:135
#63 0x00005555555bd591 in nom::branch::alt::{closure#0}<&[u8], core::result::Result<(lopdf::xref::Xref, lopdf::object::Dictionary), lopdf::error::Error>, (), (nom::combinator::map::{closure_env#0}<&[u8], (lopdf::xref::Xref, lopdf::object::Dictionary), core::result::Result<(lopdf::xref::Xref, lopdf::object::Dictionary), lopdf::error::Error>, (), nom::sequence::pair::{closure_env#0}<&[u8], lopdf::xref::Xref, lopdf::object::Dictionary, (), fn(&[u8]) -> core::result::Result<(&[u8], lopdf::xref::Xref), nom::internal::Err<()>>, fn(&[u8]) -> core::result::Result<(&[u8], lopdf::object::Dictionary), nom::internal::Err<()>>>, lopdf::parser::xref_and_trailer::{closure_env#0}>, lopdf::parser::xref_and_trailer::{closure_env#1})> (i=...)
    at /home/rafal/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nom-7.1.3/src/branch/mod.rs:50
#64 lopdf::parser::xref_and_trailer (input=..., reader=0x7fffffffce60) at src/nom_parser.rs:419
#65 lopdf::reader::Reader::read (self=..., filter_func=...) at src/reader.rs:239
#66 0x00005555555a1be6 in lopdf::document::Document::load_internal<std::io::cursor::Cursor<alloc::vec::Vec<u8, alloc::alloc::Global>>> (source=..., capacity=..., filter_func=...)
    at /home/rafal/.cargo/git/checkouts/lopdf-0013854ec52a6141/c25cef3/src/reader.rs:60
#67 0x00005555555a0f6b in lopdf::document::Document::load_from<std::io::cursor::Cursor<alloc::vec::Vec<u8, alloc::alloc::Global>>> (source=...)
    at /home/rafal/.cargo/git/checkouts/lopdf-0013854ec52a6141/c25cef3/src/reader.rs:51
#68 lopdf::check_file (file_path=...) at src/main.rs:32
#69 0x00005555555a0bf6 in lopdf::main () at src/main.rs:23

```

[timeout-66b9777e3d332374cb4d4abc3db6b840e40c496a.zip](https://github.com/user-attachments/files/16607886/timeout-66b9777e3d332374cb4d4abc3db6b840e40c496a.zip)



Discussion / hints:

Fix patch:
diff --git a/src/reader.rs b/src/reader.rs
index d002024..dc0dda7 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -230,13 +230,18 @@ impl<'a> Reader<'a> {
         let (mut xref, mut trailer) = parser::xref_and_trailer(&self.buffer[xref_start..], &self)?;
 
         // Read previous Xrefs of linearized or incremental updated document.
-        let mut prev_xref_start = trailer.remove(b"Prev");
-        while let Some(prev) = prev_xref_start.and_then(|offset| offset.as_i64().ok()) {
+        let mut already_seen = HashSet::new();
+        let mut prev_xref_start = trailer.get(b"Prev").cloned();
+        while let Ok(prev) = prev_xref_start.and_then(|offset| offset.as_i64()) {
+            if already_seen.contains(&prev) {
+                break;
+            }
+            already_seen.insert(prev);
             if prev < 0 || prev as usize > self.buffer.len() {
                 return Err(Error::Xref(XrefError::PrevStart));
             }
 
-            let (prev_xref, mut prev_trailer) = parser::xref_and_trailer(&self.buffer[prev as usize..], &self)?;
+            let (prev_xref, prev_trailer) = parser::xref_and_trailer(&self.buffer[prev as usize..], &self)?;
             xref.merge(prev_xref);
 
             // Read xref stream in hybrid-reference file
@@ -250,7 +255,7 @@ impl<'a> Reader<'a> {
                 xref.merge(prev_xref);
             }
 
-            prev_xref_start = prev_trailer.remove(b"Prev");
+            prev_xref_start = prev_trailer.get(b"Prev").cloned();
         }
 
         let xref_entry_count = xref.max_id() + 1;


Test patch: